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
9 changes: 5 additions & 4 deletions cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ console = "0.15.7"
bytes = "1.11.1"
tar = "0.4.46"
local-ip-address = "0.6"
ahp = "0.7.0"
ahp-types = "0.7.0"
ahp = "0.9.0"
ahp-types = "0.9.0"

[build-dependencies]
serde = { version="1.0.163", features = ["derive"] }
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ async fn authenticate_from_error(
resource: resource.resource.clone(),
token: credential.access_token().to_string(),
scopes: None,
meta: None,
},
)
.await
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/agent_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ async fn probe_host(
channel: ROOT_RESOURCE_URI.to_string(),
limit: None,
cursor: None,
meta: None,
},
)
.await;
Expand Down
2 changes: 2 additions & 0 deletions cli/src/commands/agent_ps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ async fn list_sessions(
channel: ROOT_RESOURCE_URI.to_string(),
limit: None,
cursor,
meta: None,
},
)
.await?;
Expand Down Expand Up @@ -417,6 +418,7 @@ mod tests {
modified_at: modified_at.to_string(),
project: None,
working_directories: None,
origin: None,
changes: None,
annotations: None,
meta: None,
Expand Down
8 changes: 5 additions & 3 deletions src/vs/platform/agentHost/browser/agentHostProtocolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import { computeReconnectDelay, DEFAULT_RECONNECT_POLICY, hasExhaustedReconnectA
import type { IRemoteAgentHostProtocolClient } from '../common/remoteAgentHostService.js';

const AHP_CLIENT_CONNECTION_CLOSED = -32000;
// AHP 0.9 changed the automation catalog wire shape, so VS Code cannot safely negotiate 0.8.
const CLIENT_SUPPORTED_PROTOCOL_VERSIONS = SUPPORTED_PROTOCOL_VERSIONS.filter(version => version !== '0.8.0');

/**
* After this much inbound silence, send an application-level `ping` to
Expand Down Expand Up @@ -469,10 +471,10 @@ export class AgentHostProtocolClient extends Disposable implements IAgentConnect

const result = await this._dispatchRequest<IAgentHostExtensionInitializeResult>('initialize', {
channel: ROOT_STATE_URI,
// Advertise every version this client can negotiate, most-preferred first, so an
// Advertise every compatible version, most-preferred first, so an
// older host (a cloud sandbox running a 0.5.x `copilotd`) can negotiate down
// instead of rejecting the connection. A current host still picks the newest.
protocolVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
protocolVersions: [...CLIENT_SUPPORTED_PROTOCOL_VERSIONS],
clientId: this._clientId,
clientInfo: this._clientInfo,
_meta: this._clientMeta(),
Expand Down Expand Up @@ -761,7 +763,7 @@ export class AgentHostProtocolClient extends Disposable implements IAgentConnect
this._logService.info(`[RemoteAgentHostProtocol] Server forgot client ${this._clientId}; initializing a fresh connection.`);
const initializeResult = await this._dispatchRequest<IAgentHostExtensionInitializeResult>('initialize', {
channel: ROOT_STATE_URI,
protocolVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
protocolVersions: [...CLIENT_SUPPORTED_PROTOCOL_VERSIONS],
clientId: this._clientId,
clientInfo: this._clientInfo,
_meta: this._clientMeta(),
Expand Down
14 changes: 11 additions & 3 deletions src/vs/platform/agentHost/common/state/agentSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ActionEnvelope, ActionType, type AutomationAction, type AutomationRunAc
import { automationReducer, automationRunReducer, changesetReducer, chatReducer, annotationsReducer, rootReducer, sessionReducer } from './sessionReducers.js';
import { terminalReducer } from './protocol/reducers.js';
import type { RootAction, SessionAction as IProtocolSessionAction, ChatAction as IProtocolChatAction, TerminalAction } from './protocol/action-origin.generated.js';
import type { AnnotationsState, AutomationCatalogState, AutomationRunState, ChangesetState, ChatState, RootState, SessionState, TerminalState } from './protocol/state.js';
import type { AnnotationsState, AutomationRunState, AutomationState, ChangesetState, ChatState, RootState, SessionState, TerminalState } from './protocol/state.js';
import type { IStateSnapshot } from './sessionProtocol.js';
import { isAhpAutomationCatalogChannel, isAhpAutomationRunChannel, isAhpRootChannel, ROOT_STATE_URI, StateComponents } from './sessionState.js';
import { normalizeLegacyChatStateErrors } from './legacyProtocolCompatibility.js';
Expand Down Expand Up @@ -577,13 +577,13 @@ export class TerminalStateSubscription extends BaseAgentSubscription<TerminalSta
}

/** Subscription to the singleton host-owned automation catalogue. */
export class AutomationCatalogSubscription extends BaseAgentSubscription<AutomationCatalogState> {
export class AutomationCatalogSubscription extends BaseAgentSubscription<AutomationState> {

constructor(clientId: string, log: (msg: string) => void) {
super(clientId, log);
}

protected override _applyReducer(state: AutomationCatalogState, action: StateAction): AutomationCatalogState {
protected override _applyReducer(state: AutomationState, action: StateAction): AutomationState {
return automationReducer(state, action as AutomationAction, this._log);
}

Expand Down Expand Up @@ -1254,6 +1254,14 @@ export function isActionEnvelopeRelevantToSubscriptionUris(envelope: ActionEnvel
}
return false;
}
if (isAhpAutomationCatalogChannel(envelope.channel)) {
for (const uri of subscribedUris) {
if (isAhpAutomationCatalogChannel(uri)) {
return true;
}
}
return false;
}
for (const uri of subscribedUris) {
if (uri === envelope.channel) {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a0bc67f8
60706330
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 type { ErrorInfo, URI, UsageInfo } from '../common/state.js';
import type { AutomationEventTrigger, AutomationMisfirePolicy, AutomationScheduleTrigger, AutomationState } from '../channels-automation/state.js';
import type { AutomationEventTrigger, AutomationMisfirePolicy, AutomationScheduleTrigger, AutomationEntry } from '../channels-automation/state.js';
import type { RunAutomationParams } from '../channels-automation/commands.js';
import type { SessionState } from '../channels-session/state.js';

Expand Down Expand Up @@ -227,7 +227,7 @@ export interface AutomationRunSummary {
export interface AutomationRunState {
/** URI of this automation-run channel. */
resource: URI;
/** Owning `ahp-automation:` URI matching {@link AutomationState.resource}. */
/** Owning `ahp-automation:` URI matching {@link AutomationEntry.resource}. */
automation: URI;
/** Immutable provenance describing how this run was created. */
origin: AutomationRunOrigin;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { ActionType } from '../common/actions.js';
import type { Message } from '../channels-chat/state.js';
import type { URI } from '../common/state.js';
import type { AutomationCatalogState, AutomationDefinition, AutomationOperation, AutomationSessionTemplate, AutomationState, AutomationTrigger } from './state.js';
import type { AutomationDefinition, AutomationEntry, AutomationOperation, AutomationSessionTemplate, AutomationState, AutomationTrigger } from './state.js';

/**
* Partial replacement of editable {@link AutomationDefinition} fields.
Expand Down Expand Up @@ -60,9 +60,9 @@ export interface AutomationDefinitionPatch {
*/
export interface AutomationCreateRequestedAction {
type: ActionType.AutomationCreateRequested;
/** Client-chosen `ahp-automation:` URI that becomes {@link AutomationState.resource}. */
/** Client-chosen `ahp-automation:` URI that becomes {@link AutomationEntry.resource}. */
resource: URI;
/** Complete initial {@link AutomationState.definition}. */
/** Complete initial {@link AutomationEntry.definition}. */
definition: AutomationDefinition;
}

Expand All @@ -87,17 +87,17 @@ export interface AutomationCreateRequestedAction {
*/
export interface AutomationUpdateRequestedAction {
type: ActionType.AutomationUpdateRequested;
/** Target {@link AutomationState.resource}. */
/** Target {@link AutomationEntry.resource}. */
resource: URI;
/** Editable {@link AutomationDefinition} fields to replace. */
changes: AutomationDefinitionPatch;
}

/**
* Add or replace one full automation state in
* {@link AutomationCatalogState.automations}.
* {@link AutomationState.entries}.
*
* Existing entries are matched by {@link AutomationState.resource} and
* Existing entries are matched by {@link AutomationEntry.resource} and
* replaced in place. A previously unseen resource is appended.
*
* @category Automation Actions
Expand All @@ -106,11 +106,11 @@ export interface AutomationUpdateRequestedAction {
export interface AutomationSetAction {
type: ActionType.AutomationSet;
/** Full new or replacement automation state. */
automation: AutomationState;
automation: AutomationEntry;
}

/**
* Remove one automation from {@link AutomationCatalogState.automations}.
* Remove one automation from {@link AutomationState.entries}.
*
* Clients may dispatch this action only while the target advertises
* {@link AutomationOperation.Remove}. The host revalidates that operation
Expand All @@ -125,6 +125,6 @@ export interface AutomationSetAction {
*/
export interface AutomationRemovedAction {
type: ActionType.AutomationRemoved;
/** {@link AutomationState.resource} to remove. */
/** {@link AutomationEntry.resource} to remove. */
resource: URI;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { URI } from '../common/state.js';
import type { AutomationRunState } from '../channels-automation-run/state.js';
import type { AgentInfo } from '../channels-root/state.js';
import type { AutomationSetAction } from './actions.js';
import type { AutomationDefinition, AutomationSessionTemplate, AutomationState, AutomationTriggerDefinition } from './state.js';
import type { AutomationDefinition, AutomationEntry, AutomationSessionTemplate, AutomationTriggerDefinition } from './state.js';

/**
* Discover event-trigger types available for a prospective session template.
Expand Down Expand Up @@ -63,8 +63,8 @@ export interface ListAutomationTriggerDefinitionsResult {
*/
export interface RunAutomationParams extends BaseParams {
/** Manual runs are scoped to the catalogue channel. */
channel: 'ahp-automations://catalog';
/** Target {@link AutomationState.resource}. */
channel: 'ahp-automations://';
/** Target {@link AutomationEntry.resource}. */
automation: URI;
/**
* Durable client-generated idempotency key. Retrying with the same key and
Expand All @@ -89,7 +89,7 @@ export interface RunAutomationResult {
*
* The response only acknowledges the request. The updated full state arrives
* through {@link AutomationSetAction | `automation/set`} on the
* `ahp-automations://catalog` channel, keeping all catalogue subscribers synchronized
* `ahp-automations://` channel, keeping all catalogue subscribers synchronized
* through the normal action stream.
*
* @category Commands
Expand All @@ -100,11 +100,11 @@ export interface RunAutomationResult {
*/
export interface FetchAutomationRunsParams extends BaseParams {
/** Run-history loading is scoped to the catalogue channel. */
channel: 'ahp-automations://catalog';
/** Target {@link AutomationState.resource}. */
channel: 'ahp-automations://';
/** Target {@link AutomationEntry.resource}. */
automation: URI;
/**
* Cursor previously received as {@link AutomationState.runsNextCursor}.
* Cursor previously received as {@link AutomationEntry.runsNextCursor}.
* Omit to request the first page not already included by the snapshot.
*/
cursor?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,36 @@
import type { AutomationAction } from '../action-origin.generated.js';
import { ActionType } from '../common/actions.js';
import { softAssertNever } from '../common/reducer-helpers.js';
import type { AutomationCatalogState } from './state.js';
import type { AutomationState } from './state.js';

/** Pure reducer for automation catalogue state. */
export function automationReducer(state: AutomationCatalogState, action: AutomationAction, log?: (msg: string) => void): AutomationCatalogState {
export function automationReducer(state: AutomationState, action: AutomationAction, log?: (msg: string) => void): AutomationState {
switch (action.type) {
case ActionType.AutomationCreateRequested:
case ActionType.AutomationUpdateRequested:
return state;

case ActionType.AutomationSet: {
const idx = state.automations.findIndex(automation => automation.resource === action.automation.resource);
const idx = state.entries.findIndex(automation => automation.resource === action.automation.resource);
if (idx < 0) {
return {
...state,
automations: [...state.automations, action.automation],
entries: [...state.entries, action.automation],
};
}
const automations = state.automations.slice();
automations[idx] = action.automation;
return { ...state, automations };
const entries = state.entries.slice();
entries[idx] = action.automation;
return { ...state, entries };
}

case ActionType.AutomationRemoved: {
const idx = state.automations.findIndex(automation => automation.resource === action.resource);
const idx = state.entries.findIndex(automation => automation.resource === action.resource);
if (idx < 0) {
return state;
}
const automations = state.automations.slice();
automations.splice(idx, 1);
return { ...state, automations };
const entries = state.entries.slice();
entries.splice(idx, 1);
return { ...state, entries };
}

default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type { FetchAutomationRunsParams, ListAutomationTriggerDefinitionsParams,
/**
* Operations the host currently permits for an automation.
*
* The list on {@link AutomationState.operations} is authoritative and may
* The list on {@link AutomationEntry.operations} is authoritative and may
* change over time. Clients MUST NOT infer permission from capabilities alone:
* capabilities describe what the host implementation can support, while
* operations describe what is allowed for this particular automation now.
Expand Down Expand Up @@ -255,7 +255,7 @@ export interface AutomationSessionTemplate {
* A definition combines the initial automation message, the session template
* used for each run, and zero or more automatic triggers. Run history,
* timestamps, and currently allowed operations live on
* {@link AutomationState} rather than in the definition.
* {@link AutomationEntry} rather than in the definition.
*
* @category Automation State
*/
Expand Down Expand Up @@ -284,16 +284,15 @@ export interface AutomationDefinition {
}

/**
* Authoritative state of one automation in the
* {@link AutomationCatalogState.automations} catalogue.
* Authoritative state of one automation in {@link AutomationState.entries}.
*
* The host owns trigger evaluation, run claims, run retention, and operation
* availability. Clients render this state and submit actions or commands; they
* never run a fallback scheduler for a host-owned definition.
*
* @category Automation State
*/
export interface AutomationState {
export interface AutomationEntry {
/** Stable `ahp-automation:/<id>` resource identifier. */
resource: URI;
/** Current durable definition. */
Expand All @@ -303,7 +302,7 @@ export interface AutomationState {
/**
* Newest-first retained run summaries. This is a bounded window; use
* {@link FetchAutomationRunsParams | fetchAutomationRuns} when
* {@link AutomationState.runsNextCursor} is present.
* {@link AutomationEntry.runsNextCursor} is present.
*/
runs: AutomationRunSummary[];
/** Opaque cursor passed as {@link FetchAutomationRunsParams.cursor} for the next older run-history page. */
Expand All @@ -319,7 +318,7 @@ export interface AutomationState {
}

/**
* Authoritative automation catalogue exposed on the `ahp-automations://catalog`
* Authoritative automation catalogue exposed on the `ahp-automations://`
* channel.
*
* A subscription snapshot contains every automation visible to the client.
Expand All @@ -329,9 +328,9 @@ export interface AutomationState {
*
* @category Automation State
*/
export interface AutomationCatalogState {
/** Full automation states keyed by {@link AutomationState.resource}. */
automations: AutomationState[];
export interface AutomationState {
/** Full automation entries keyed by {@link AutomationEntry.resource}. */
entries: AutomationEntry[];
/** Opaque host-defined catalogue metadata. */
_meta?: Record<string, unknown>;
}
Loading