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
21 changes: 20 additions & 1 deletion src/vs/platform/agentHost/common/agentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js';
import type { ISyncedCustomization } from './agentPluginManager.js';
import type { IAgentSubscription } from './state/agentSubscription.js';
import type { CompletionsParams, CompletionsResult, CreateTerminalParams, ResolveSessionConfigResult, SessionConfigCompletionsResult } from './state/protocol/commands.js';
import { ProtectedResourceMetadata, type ChangesetSummary, type ConfigSchema, type MessageAttachment, type ModelSelection, type SessionActiveClient, type ToolCallPendingConfirmationState, type ToolDefinition } from './state/protocol/state.js';
import { ProtectedResourceMetadata, type ChangesetSummary, type ConfigSchema, type MessageAttachment, type ModelSelection, type AgentSelection, type SessionActiveClient, type ToolCallPendingConfirmationState, type ToolDefinition } from './state/protocol/state.js';
import type { ActionEnvelope, INotification, IRootConfigChangedAction, SessionAction, TerminalAction } from './state/sessionActions.js';
import type { ResourceCopyParams, ResourceCopyResult, ResourceDeleteParams, ResourceDeleteResult, ResourceListResult, ResourceMoveParams, ResourceMoveResult, ResourceReadResult, ResourceWriteParams, ResourceWriteResult, IStateSnapshot } from './state/sessionProtocol.js';
import { ComponentToState, SessionInputResponseKind, SessionStatus, StateComponents, type CustomizationRef, type PendingMessage, type RootState, type SessionCustomization, type SessionInputAnswer, type SessionMeta, type ToolCallResult, type Turn, type PolicyState } from './state/sessionState.js';
Expand Down Expand Up @@ -222,6 +222,12 @@ export interface IAgentSessionMetadata {
/** Human-readable description of what the session is currently doing. */
readonly activity?: string;
readonly model?: ModelSelection;
/**
* Selected custom agent for this session. Absent (`undefined`) means no
* custom agent is selected — the session uses the provider's default
* behavior.
*/
readonly agent?: AgentSelection;
readonly workingDirectory?: URI;
readonly customizationDirectory?: URI;
readonly isRead?: boolean;
Expand Down Expand Up @@ -331,6 +337,11 @@ export const GITHUB_COPILOT_PROTECTED_RESOURCE: ProtectedResourceMetadata = {
export interface IAgentCreateSessionConfig {
readonly provider?: AgentProvider;
readonly model?: ModelSelection;
/**
* Initial custom agent selection for the new session. Omit to start with
* no custom agent selected (provider default behavior).
*/
readonly agent?: AgentSelection;
readonly session?: URI;
readonly workingDirectory?: URI;
readonly config?: Record<string, unknown>;
Expand Down Expand Up @@ -581,6 +592,14 @@ export interface IAgent {
/** Change the model for an existing session. */
changeModel(session: URI, model: ModelSelection): Promise<void>;

/**
* Change (or clear) the selected custom agent for an existing session.
* Passing `undefined` clears the selection and resets the session to no
* selected custom agent (provider default behavior). Optional so non-
* Copilot agents can opt out.
*/
changeAgent?(session: URI, agent: AgentSelection | undefined): Promise<void>;

/** Respond to a pending permission request from the SDK. */
respondToPermissionRequest(requestId: string, approved: boolean): void;

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
646a491
ff84a6b
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 SessionServerToolsChangedAction, type SessionActiveClientChangedAction, type SessionActiveClientToolsChangedAction, type SessionPendingMessageSetAction, type SessionPendingMessageRemovedAction, type SessionQueuedMessagesReorderedAction, type SessionInputRequestedAction, type SessionInputAnswerChangedAction, type SessionInputCompletedAction, type SessionCustomizationsChangedAction, type SessionCustomizationToggledAction, type SessionCustomizationUpdatedAction, 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 } 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 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 } from './actions.js';


// ─── Root vs Session vs Terminal vs Changeset Action Unions ─────────────────
Expand Down Expand Up @@ -55,6 +55,7 @@ export type SessionAction =
| SessionUsageAction
| SessionReasoningAction
| SessionModelChangedAction
| SessionAgentChangedAction
| SessionServerToolsChangedAction
| SessionActiveClientChangedAction
| SessionActiveClientToolsChangedAction
Expand Down Expand Up @@ -86,6 +87,7 @@ export type ClientSessionAction =
| SessionTurnCancelledAction
| SessionTitleChangedAction
| SessionModelChangedAction
| SessionAgentChangedAction
| SessionActiveClientChangedAction
| SessionActiveClientToolsChangedAction
| SessionPendingMessageSetAction
Expand Down Expand Up @@ -209,6 +211,7 @@ export const IS_CLIENT_DISPATCHABLE: { readonly [K in StateAction['type']]: bool
[ActionType.SessionUsage]: false,
[ActionType.SessionReasoning]: false,
[ActionType.SessionModelChanged]: true,
[ActionType.SessionAgentChanged]: true,
[ActionType.SessionServerToolsChanged]: false,
[ActionType.SessionActiveClientChanged]: true,
[ActionType.SessionActiveClientToolsChanged]: 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 { URI, StringOrMarkdown, ErrorInfo, FileEdit, UsageInfo } from '../common/state.js';
import { ToolCallConfirmationReason, ToolCallCancellationReason, PendingMessageKind, type UserMessage, type ResponsePart, type ToolCallResult, type ToolResultContent, type ToolDefinition, type SessionActiveClient, type SessionCustomization, type CustomizationRef, type SessionInputAnswer, type SessionInputRequest, type SessionInputResponseKind, type ConfirmationOption, type CustomizationStatus } from './state.js';
import { ToolCallConfirmationReason, ToolCallCancellationReason, PendingMessageKind, type UserMessage, type ResponsePart, type ToolCallResult, type ToolResultContent, type ToolDefinition, type SessionActiveClient, type SessionCustomization, type CustomizationRef, type CustomizationAgentRef, type SessionInputAnswer, type SessionInputRequest, type SessionInputResponseKind, type ConfirmationOption, type CustomizationStatus, type AgentSelection } from './state.js';
import type { ModelSelection } from '../channels-root/state.js';
import type { ChangesetSummary } from '../channels-changeset/state.js';

Expand Down Expand Up @@ -403,6 +403,29 @@ export interface SessionModelChangedAction {
model: ModelSelection;
}

/**
* Custom agent selection changed for this session.
*
* Omitting `agent` (or setting it to `undefined`) clears the selection and
* resets the session to no selected custom agent (provider default behavior).
*
* When a turn is currently active, the server MUST defer the change until
* the active turn completes, then apply it for the next turn (same rule as
* {@link SessionModelChangedAction | `session/modelChanged`}).
*
* @category Session Actions
* @version 1
* @clientDispatchable
*/
export interface SessionAgentChangedAction {
type: ActionType.SessionAgentChanged;
/**
* New agent selection, or `undefined` to clear the selection and reset the
* session to no selected custom agent.
*/
agent?: AgentSelection;
}

/**
* The read state of the session changed.
*
Expand Down Expand Up @@ -582,6 +605,12 @@ export interface SessionCustomizationUpdatedAction {
status?: CustomizationStatus;
/** New human-readable status detail */
statusMessage?: string;
/**
* Custom agents contributed by this customization, as resolved by the
* agent host. Populated only by the agent host. See
* {@link SessionCustomization.agents} for absent-vs-empty semantics.
*/
agents?: CustomizationAgentRef[];
}

// ─── Config Actions ──────────────────────────────────────────────────────────
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import type { URI } from '../common/state.js';
import type { BaseParams } from '../common/commands.js';
import type { ModelSelection } from '../channels-root/state.js';
import type { Turn, SessionActiveClient, MessageAttachment } from './state.js';
import type { Turn, SessionActiveClient, MessageAttachment, AgentSelection } from './state.js';

// ─── createSession ───────────────────────────────────────────────────────────

Expand Down Expand Up @@ -65,6 +65,12 @@ export interface CreateSessionParams extends BaseParams {
provider?: string;
/** Model selection (ID and optional model-specific configuration) */
model?: ModelSelection;
/**
* Initial custom agent selection for the new session.
*
* Omit to start the session with no custom agent selected (provider default).
*/
agent?: AgentSelection;
/** Working directory for the session */
workingDirectory?: URI;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ export function sessionReducer(state: SessionState, action: SessionAction, log?:
summary: { ...state.summary, model: action.model, modifiedAt: Date.now() },
};

case ActionType.SessionAgentChanged:
return {
...state,
summary: { ...state.summary, agent: action.agent, modifiedAt: Date.now() },
};

case ActionType.SessionIsReadChanged:
return {
...state,
Expand Down Expand Up @@ -616,6 +622,9 @@ export function sessionReducer(state: SessionState, action: SessionAction, log?:
if (action.statusMessage !== undefined) {
inserted.statusMessage = action.statusMessage;
}
if (action.agents !== undefined) {
inserted.agents = action.agents;
}
return { ...state, customizations: [...list, inserted] };
}
const updated = [...list];
Expand All @@ -629,6 +638,9 @@ export function sessionReducer(state: SessionState, action: SessionAction, log?:
if (action.statusMessage !== undefined) {
next.statusMessage = action.statusMessage;
}
if (action.agents !== undefined) {
next.agents = action.agents;
}
updated[idx] = next;
return { ...state, customizations: updated };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ export interface SessionSummary {
project?: ProjectInfo;
/** Currently selected model */
model?: ModelSelection;
/**
* Currently selected custom agent.
*
* Absent (`undefined`) means no custom agent is selected for this session
* — the session uses the provider's default behavior.
*/
agent?: AgentSelection;
/** The working directory URI for this session */
workingDirectory?: URI;
/**
Expand All @@ -191,6 +198,26 @@ export interface SessionSummary {
// `ModelSelection` is declared in channels-root/state.ts (the model lives on
// `AgentInfo`); we import it above for use in `SessionSummary.model`.

// ─── Agent Selection ─────────────────────────────────────────────────────────

/**
* A selected custom agent for a session.
*
* The `uri` identifies a specific custom agent (matching a
* {@link CustomizationAgentRef.uri | `CustomizationAgentRef.uri`} exposed
* via the session's effective customizations). Consumers resolve the
* agent's display name by looking up `uri` in
* {@link SessionCustomization.agents | `SessionCustomization.agents`}.
*
* A session with no `agent` selected uses the provider's default behavior.
*
* @category Session State
*/
export interface AgentSelection {
/** Stable agent URI (matches a {@link CustomizationAgentRef.uri}) */
uri: URI;
}

// ─── Session Config Types ────────────────────────────────────────────────────

/**
Expand Down Expand Up @@ -1227,6 +1254,24 @@ export type ToolResultContent =

// ─── Customization Types ─────────────────────────────────────────────────────

/**
* A lightweight reference to a custom agent contributed by a customization.
*
* Custom agents have a single `name` (sourced from the agent file's YAML
* frontmatter, or derived from the file name); they do not have a separate
* display name.
*
* @category Customization Types
*/
export interface CustomizationAgentRef {
/** Stable agent URI */
uri: URI;
/** Agent name (from frontmatter `name`, or file-derived) */
name: string;
/** Optional short description for UI preview (from frontmatter `description`) */
description?: string;
}

/**
* A reference to an [Open Plugins](https://open-plugins.com/) plugin.
*
Expand Down Expand Up @@ -1291,4 +1336,16 @@ export interface SessionCustomization {
* Human-readable status detail (e.g. error message or degradation warning).
*/
statusMessage?: string;
/**
* Custom agents contributed by this customization, as resolved by the
* agent host after parsing the customization.
*
* Consumers MUST treat an absent field as "unknown" (e.g. the host has
* not finished parsing the customization yet). An empty array means the
* host parsed the customization and it contributes no agents.
*
* Clients are not authoritative here: only the agent host populates
* this field.
*/
agents?: CustomizationAgentRef[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { URI } from './state.js';

import type { RootAgentsChangedAction, RootActiveSessionsChangedAction, RootTerminalsChangedAction, RootConfigChangedAction } from '../channels-root/actions.js';

import type { SessionReadyAction, SessionCreationFailedAction, SessionTurnStartedAction, SessionDeltaAction, SessionResponsePartAction, SessionToolCallStartAction, SessionToolCallDeltaAction, SessionToolCallReadyAction, SessionToolCallConfirmedAction, SessionToolCallCompleteAction, SessionToolCallResultConfirmedAction, SessionToolCallContentChangedAction, SessionTurnCompleteAction, SessionTurnCancelledAction, SessionErrorAction, SessionTitleChangedAction, SessionUsageAction, SessionReasoningAction, SessionModelChangedAction, SessionServerToolsChangedAction, SessionActiveClientChangedAction, SessionActiveClientToolsChangedAction, SessionPendingMessageSetAction, SessionPendingMessageRemovedAction, SessionQueuedMessagesReorderedAction, SessionInputRequestedAction, SessionInputAnswerChangedAction, SessionInputCompletedAction, SessionCustomizationsChangedAction, SessionCustomizationToggledAction, SessionCustomizationUpdatedAction, SessionTruncatedAction, SessionIsReadChangedAction, SessionIsArchivedChangedAction, SessionActivityChangedAction, SessionChangesetsChangedAction, SessionConfigChangedAction, SessionMetaChangedAction } from '../channels-session/actions.js';
import type { SessionReadyAction, SessionCreationFailedAction, SessionTurnStartedAction, SessionDeltaAction, SessionResponsePartAction, SessionToolCallStartAction, SessionToolCallDeltaAction, SessionToolCallReadyAction, SessionToolCallConfirmedAction, SessionToolCallCompleteAction, SessionToolCallResultConfirmedAction, SessionToolCallContentChangedAction, SessionTurnCompleteAction, SessionTurnCancelledAction, SessionErrorAction, SessionTitleChangedAction, SessionUsageAction, SessionReasoningAction, SessionModelChangedAction, SessionAgentChangedAction, SessionServerToolsChangedAction, SessionActiveClientChangedAction, SessionActiveClientToolsChangedAction, SessionPendingMessageSetAction, SessionPendingMessageRemovedAction, SessionQueuedMessagesReorderedAction, SessionInputRequestedAction, SessionInputAnswerChangedAction, SessionInputCompletedAction, SessionCustomizationsChangedAction, SessionCustomizationToggledAction, SessionCustomizationUpdatedAction, SessionTruncatedAction, SessionIsReadChangedAction, SessionIsArchivedChangedAction, SessionActivityChangedAction, SessionChangesetsChangedAction, SessionConfigChangedAction, SessionMetaChangedAction } from '../channels-session/actions.js';

import type { ChangesetStatusChangedAction, ChangesetFileSetAction, ChangesetFileRemovedAction, ChangesetOperationsChangedAction, ChangesetClearedAction } from '../channels-changeset/actions.js';

Expand Down Expand Up @@ -45,6 +45,7 @@ export const enum ActionType {
SessionUsage = 'session/usage',
SessionReasoning = 'session/reasoning',
SessionModelChanged = 'session/modelChanged',
SessionAgentChanged = 'session/agentChanged',
SessionServerToolsChanged = 'session/serverToolsChanged',
SessionActiveClientChanged = 'session/activeClientChanged',
SessionActiveClientToolsChanged = 'session/activeClientToolsChanged',
Expand Down Expand Up @@ -141,6 +142,7 @@ export type StateAction =
| SessionUsageAction
| SessionReasoningAction
| SessionModelChangedAction
| SessionAgentChangedAction
| SessionServerToolsChangedAction
| SessionActiveClientChangedAction
| SessionActiveClientToolsChangedAction
Expand Down
Loading
Loading