diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index b95ab82f1..4b6cdab93 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -821,6 +821,145 @@ internal sealed class SkillsConfigSetDisabledSkillsRequest public IList DisabledSkills { get => field ??= []; set; } } +/// Schema for the `AgentInfo` type. +[Experimental(Diagnostics.Experimental)] +public sealed class AgentInfo +{ + /// Description of the agent's purpose. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Human-readable display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; + + /// Stable identifier for selection. For most agents this is the same as `name`; for plugin/builtin agents it may differ. Always populated; defaults to `name` when no distinct id was assigned. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. + [Experimental(Diagnostics.Experimental)] + [JsonPropertyName("mcpServers")] + public IDictionary? McpServers { get; set; } + + /// Preferred model id for this agent. When omitted, inherits the outer agent's model. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Unique identifier of the custom agent. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Absolute local file path of the agent definition. Only set for file-based agents loaded from disk; remote agents do not have a path. + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// Skill names preloaded into this agent's context. Omitted means none. + [JsonPropertyName("skills")] + public IList? Skills { get; set; } + + /// Where the agent definition was loaded from. + [JsonPropertyName("source")] + public AgentInfoSource? Source { get; set; } + + /// Allowed tool names for this agent. Empty array means none; omitted means inherit defaults. + [JsonPropertyName("tools")] + public IList? Tools { get; set; } + + /// Whether the agent can be selected directly by the user. Agents marked `false` are subagent-only. + [JsonPropertyName("userInvocable")] + public bool? UserInvocable { get; set; } +} + +/// Agents discovered across user, project, plugin, and remote sources. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerAgentList +{ + /// All discovered agents across all sources. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } +} + +/// Optional project paths to include in agent discovery. +[Experimental(Diagnostics.Experimental)] +internal sealed class AgentsDiscoverRequest +{ + /// When true, omit the host's agents (the `<COPILOT_HOME>/agents` directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + [JsonPropertyName("excludeHostAgents")] + public bool? ExcludeHostAgents { get; set; } + + /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } +} + +/// Schema for the `InstructionSource` type. +[Experimental(Diagnostics.Experimental)] +public sealed class InstructionSource +{ + /// Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files. + [JsonPropertyName("applyTo")] + public IList? ApplyTo { get; set; } + + /// Raw content of the instruction file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// When true, this source starts disabled and must be toggled on by the user. + [JsonPropertyName("defaultDisabled")] + public bool? DefaultDisabled { get; set; } + + /// Short description (body after frontmatter) for use in instruction tables. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Unique identifier for this source (used for toggling). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Human-readable label. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; + + /// Where this source lives — used for UI grouping. + [JsonPropertyName("location")] + public InstructionSourceLocation Location { get; set; } + + /// The project path this source was discovered from. Only set by sessionless discovery for repository/working-directory sources, where it disambiguates same-named files (e.g. .github/copilot-instructions.md) across multiple workspace roots. The session-scoped getSources leaves it unset. + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } + + /// File path relative to repo or absolute for home. + [JsonPropertyName("sourcePath")] + public string SourcePath { get; set; } = string.Empty; + + /// Category of instruction source — used for merge logic. + [JsonPropertyName("type")] + public InstructionSourceType Type { get; set; } +} + +/// Instruction sources discovered across user, repository, and plugin sources. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerInstructionSourceList +{ + /// All discovered instruction sources. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } +} + +/// Optional project paths to include in instruction discovery. +[Experimental(Diagnostics.Experimental)] +internal sealed class InstructionsDiscoverRequest +{ + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + [JsonPropertyName("excludeHostInstructions")] + public bool? ExcludeHostInstructions { get; set; } + + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; set; } +} + /// Indicates whether the calling client was registered as the session filesystem provider. public sealed class SessionFsSetProviderResult { @@ -3795,54 +3934,13 @@ internal sealed class WorkspacesDiffRequest public string SessionId { get; set; } = string.Empty; } -/// Schema for the `InstructionsSources` type. -[Experimental(Diagnostics.Experimental)] -public sealed class InstructionsSources -{ - /// Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files. - [JsonPropertyName("applyTo")] - public IList? ApplyTo { get; set; } - - /// Raw content of the instruction file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; - - /// When true, this source starts disabled and must be toggled on by the user. - [JsonPropertyName("defaultDisabled")] - public bool? DefaultDisabled { get; set; } - - /// Short description (body after frontmatter) for use in instruction tables. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Unique identifier for this source (used for toggling). - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Human-readable label. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; - - /// Where this source lives — used for UI grouping. - [JsonPropertyName("location")] - public InstructionsSourcesLocation Location { get; set; } - - /// File path relative to repo or absolute for home. - [JsonPropertyName("sourcePath")] - public string SourcePath { get; set; } = string.Empty; - - /// Category of instruction source — used for merge logic. - [JsonPropertyName("type")] - public InstructionsSourcesType Type { get; set; } -} - /// Instruction sources loaded for the session, in merge order. [Experimental(Diagnostics.Experimental)] public sealed class InstructionsGetSourcesResult { /// Instruction sources for the session. [JsonPropertyName("sources")] - public IList Sources { get => field ??= []; set; } + public IList Sources { get => field ??= []; set; } } /// Identifies the target session. @@ -3876,56 +3974,6 @@ internal sealed class FleetStartRequest public string SessionId { get; set; } = string.Empty; } -/// Schema for the `AgentInfo` type. -[Experimental(Diagnostics.Experimental)] -public sealed class AgentInfo -{ - /// Description of the agent's purpose. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Human-readable display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Stable identifier for selection. For most agents this is the same as `name`; for plugin/builtin agents it may differ. Always populated; defaults to `name` when no distinct id was assigned. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// MCP server configurations attached to this agent, keyed by server name. Server config shape mirrors the MCP `mcpServers` schema. - [Experimental(Diagnostics.Experimental)] - [JsonPropertyName("mcpServers")] - public IDictionary? McpServers { get; set; } - - /// Preferred model id for this agent. When omitted, inherits the outer agent's model. - [JsonPropertyName("model")] - public string? Model { get; set; } - - /// Unique identifier of the custom agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Absolute local file path of the agent definition. Only set for file-based agents loaded from disk; remote agents do not have a path. - [JsonPropertyName("path")] - public string? Path { get; set; } - - /// Skill names preloaded into this agent's context. Omitted means none. - [JsonPropertyName("skills")] - public IList? Skills { get; set; } - - /// Where the agent definition was loaded from. - [JsonPropertyName("source")] - public AgentInfoSource? Source { get; set; } - - /// Allowed tool names for this agent. Empty array means none; omitted means inherit defaults. - [JsonPropertyName("tools")] - public IList? Tools { get; set; } - - /// Whether the agent can be selected directly by the user. Agents marked `false` are subagent-only. - [JsonPropertyName("userInvocable")] - public bool? UserInvocable { get; set; } -} - /// Custom agents available to the session. [Experimental(Diagnostics.Experimental)] public sealed class AgentList @@ -4127,7 +4175,7 @@ public partial class TaskInfoAgent : TaskInfo [JsonPropertyName("latestResponse")] public string? LatestResponse { get; set; } - /// Model used for the task when specified. + /// Requested model override for the task when specified. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("model")] public string? Model { get; set; } @@ -4136,6 +4184,11 @@ public partial class TaskInfoAgent : TaskInfo [JsonPropertyName("prompt")] public required string Prompt { get; set; } + /// Runtime model resolved for the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } + /// Result text from the task when available. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("result")] @@ -4849,7 +4902,7 @@ public sealed class McpFilteredServer /// MCP server startup filtering result. [Experimental(Diagnostics.Experimental)] -public sealed class McpStartServersResult +internal sealed class McpStartServersResult { /// Non-default servers allowed by policy. [JsonPropertyName("allowedServers")] @@ -4992,7 +5045,7 @@ internal sealed class SessionMcpRemoveGitHubRequest /// Result of configuring GitHub MCP. [Experimental(Diagnostics.Experimental)] -public sealed class McpConfigureGitHubResult +internal sealed class McpConfigureGitHubResult { /// Whether GitHub MCP configuration changed. [JsonPropertyName("changed")] @@ -5127,7 +5180,7 @@ internal sealed class McpIsServerRunningRequest /// Empty result after recording the MCP OAuth response. [Experimental(Diagnostics.Experimental)] -public sealed class McpOauthRespondResult +internal sealed class McpOauthRespondResult { } @@ -9462,6 +9515,14 @@ internal sealed class RemoteNotifySteerableChangedRequest [Experimental(Diagnostics.Experimental)] public sealed class ScheduleEntry { + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + [JsonPropertyName("at")] + public long? At { get; set; } + + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + [JsonPropertyName("cron")] + public string? Cron { get; set; } + /// Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`. [JsonPropertyName("displayPrompt")] public string? DisplayPrompt { get; set; } @@ -9470,10 +9531,10 @@ public sealed class ScheduleEntry [JsonPropertyName("id")] public long Id { get; set; } - /// Interval between scheduled ticks, in milliseconds. + /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonPropertyName("intervalMs")] - public TimeSpan Interval { get; set; } + public TimeSpan? Interval { get; set; } /// ISO 8601 timestamp when the next tick is scheduled to fire. [JsonPropertyName("nextRunAt")] @@ -9486,6 +9547,10 @@ public sealed class ScheduleEntry /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). [JsonPropertyName("recurring")] public bool Recurring { get; set; } + + /// IANA timezone the `cron` expression is evaluated in. + [JsonPropertyName("tz")] + public string? Tz { get; set; } } /// Snapshot of the currently active recurring prompts for this session. @@ -10255,42 +10320,55 @@ public override void Write(Utf8JsonWriter writer, DiscoveredMcpServerType value, } -/// Path conventions used by this filesystem. +/// Where the agent definition was loaded from. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsSetProviderConventions : IEquatable +public readonly struct AgentInfoSource : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public SessionFsSetProviderConventions(string value) + public AgentInfoSource(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Paths use Windows path conventions. - public static SessionFsSetProviderConventions Windows { get; } = new("windows"); + /// Agent loaded from the user's personal agent configuration. + public static AgentInfoSource User { get; } = new("user"); - /// Paths use POSIX path conventions. - public static SessionFsSetProviderConventions Posix { get; } = new("posix"); + /// Agent loaded from the current project's repository configuration. + public static AgentInfoSource Project { get; } = new("project"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => left.Equals(right); + /// Agent inherited from a parent project or workspace. + public static AgentInfoSource Inherited { get; } = new("inherited"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => !(left == right); + /// Agent provided by a remote runtime or service. + public static AgentInfoSource Remote { get; } = new("remote"); + + /// Agent contributed by an installed plugin. + public static AgentInfoSource Plugin { get; } = new("plugin"); + + /// Agent built into the Copilot runtime. + public static AgentInfoSource Builtin { get; } = new("builtin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentInfoSource left, AgentInfoSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentInfoSource left, AgentInfoSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionFsSetProviderConventions other && Equals(other); + public override bool Equals(object? obj) => obj is AgentInfoSource other && Equals(other); /// - public bool Equals(SessionFsSetProviderConventions other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentInfoSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -10298,9 +10376,218 @@ public SessionFsSetProviderConventions(string value) /// public override string ToString() => Value; - /// Provides a for serializing instances. + /// Provides a for serializing instances. [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter + public sealed class Converter : JsonConverter + { + /// + public override AgentInfoSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, AgentInfoSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentInfoSource)); + } + } +} + + +/// Where this source lives — used for UI grouping. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionSourceLocation : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionSourceLocation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Instructions live in user-level configuration. + public static InstructionSourceLocation User { get; } = new("user"); + + /// Instructions live in repository-level configuration. + public static InstructionSourceLocation Repository { get; } = new("repository"); + + /// Instructions live under the current working directory. + public static InstructionSourceLocation WorkingDirectory { get; } = new("working-directory"); + + /// Instructions live in plugin-provided configuration. + public static InstructionSourceLocation Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionSourceLocation left, InstructionSourceLocation right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionSourceLocation left, InstructionSourceLocation right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionSourceLocation other && Equals(other); + + /// + public bool Equals(InstructionSourceLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionSourceLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionSourceLocation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceLocation)); + } + } +} + + +/// Category of instruction source — used for merge logic. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct InstructionSourceType : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public InstructionSourceType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Instructions loaded from the user's home configuration. + public static InstructionSourceType Home { get; } = new("home"); + + /// Instructions loaded from repository-scoped files. + public static InstructionSourceType Repo { get; } = new("repo"); + + /// Instructions loaded from model-specific files. + public static InstructionSourceType Model { get; } = new("model"); + + /// Instructions loaded from VS Code instruction files. + public static InstructionSourceType Vscode { get; } = new("vscode"); + + /// Instructions discovered from nested agent files. + public static InstructionSourceType NestedAgents { get; } = new("nested-agents"); + + /// Instructions inherited from child instruction files. + public static InstructionSourceType ChildInstructions { get; } = new("child-instructions"); + + /// Instructions supplied by an installed plugin. + public static InstructionSourceType Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(InstructionSourceType left, InstructionSourceType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(InstructionSourceType left, InstructionSourceType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is InstructionSourceType other && Equals(other); + + /// + public bool Equals(InstructionSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override InstructionSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, InstructionSourceType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceType)); + } + } +} + + +/// Path conventions used by this filesystem. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionFsSetProviderConventions : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionFsSetProviderConventions(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Paths use Windows path conventions. + public static SessionFsSetProviderConventions Windows { get; } = new("windows"); + + /// Paths use POSIX path conventions. + public static SessionFsSetProviderConventions Posix { get; } = new("posix"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionFsSetProviderConventions other && Equals(other); + + /// + public bool Equals(SessionFsSetProviderConventions other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter { /// public override SessionFsSetProviderConventions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -11859,228 +12146,6 @@ public override void Write(Utf8JsonWriter writer, WorkspaceDiffMode value, JsonS } -/// Where this source lives — used for UI grouping. -[Experimental(Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionsSourcesLocation : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionsSourcesLocation(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Instructions live in user-level configuration. - public static InstructionsSourcesLocation User { get; } = new("user"); - - /// Instructions live in repository-level configuration. - public static InstructionsSourcesLocation Repository { get; } = new("repository"); - - /// Instructions live under the current working directory. - public static InstructionsSourcesLocation WorkingDirectory { get; } = new("working-directory"); - - /// Instructions live in plugin-provided configuration. - public static InstructionsSourcesLocation Plugin { get; } = new("plugin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionsSourcesLocation left, InstructionsSourcesLocation right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionsSourcesLocation left, InstructionsSourcesLocation right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionsSourcesLocation other && Equals(other); - - /// - public bool Equals(InstructionsSourcesLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionsSourcesLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionsSourcesLocation value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionsSourcesLocation)); - } - } -} - - -/// Category of instruction source — used for merge logic. -[Experimental(Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionsSourcesType : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public InstructionsSourcesType(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Instructions loaded from the user's home configuration. - public static InstructionsSourcesType Home { get; } = new("home"); - - /// Instructions loaded from repository-scoped files. - public static InstructionsSourcesType Repo { get; } = new("repo"); - - /// Instructions loaded from model-specific files. - public static InstructionsSourcesType Model { get; } = new("model"); - - /// Instructions loaded from VS Code instruction files. - public static InstructionsSourcesType Vscode { get; } = new("vscode"); - - /// Instructions discovered from nested agent files. - public static InstructionsSourcesType NestedAgents { get; } = new("nested-agents"); - - /// Instructions inherited from child instruction files. - public static InstructionsSourcesType ChildInstructions { get; } = new("child-instructions"); - - /// Instructions supplied by an installed plugin. - public static InstructionsSourcesType Plugin { get; } = new("plugin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionsSourcesType left, InstructionsSourcesType right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionsSourcesType left, InstructionsSourcesType right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is InstructionsSourcesType other && Equals(other); - - /// - public bool Equals(InstructionsSourcesType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override InstructionsSourcesType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, InstructionsSourcesType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionsSourcesType)); - } - } -} - - -/// Where the agent definition was loaded from. -[Experimental(Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct AgentInfoSource : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public AgentInfoSource(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Agent loaded from the user's personal agent configuration. - public static AgentInfoSource User { get; } = new("user"); - - /// Agent loaded from the current project's repository configuration. - public static AgentInfoSource Project { get; } = new("project"); - - /// Agent inherited from a parent project or workspace. - public static AgentInfoSource Inherited { get; } = new("inherited"); - - /// Agent provided by a remote runtime or service. - public static AgentInfoSource Remote { get; } = new("remote"); - - /// Agent contributed by an installed plugin. - public static AgentInfoSource Plugin { get; } = new("plugin"); - - /// Agent built into the Copilot runtime. - public static AgentInfoSource Builtin { get; } = new("builtin"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentInfoSource left, AgentInfoSource right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentInfoSource left, AgentInfoSource right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is AgentInfoSource other && Equals(other); - - /// - public bool Equals(AgentInfoSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override AgentInfoSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, AgentInfoSource value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentInfoSource)); - } - } -} - - /// Whether task execution is synchronously awaited or managed in the background. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -15159,6 +15224,18 @@ internal async Task ConnectAsync(string? token = null, Cancellati Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// Agents APIs. + public ServerAgentsApi Agents => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + + /// Instructions APIs. + public ServerInstructionsApi Instructions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// User APIs. public ServerUserApi User => field ?? @@ -15597,6 +15674,52 @@ public async Task SetDisabledSkillsAsync(IList disabledSkills, Cancellat } } +/// Provides server-scoped Agents APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerAgentsApi +{ + private readonly JsonRpc _rpc; + + internal ServerAgentsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Discovers custom agents across user, project, plugin, and remote sources. + /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + /// When true, omit the host's agents (the `<COPILOT_HOME>/agents` directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + /// The to monitor for cancellation requests. The default is . + /// Agents discovered across user, project, plugin, and remote sources. + public async Task DiscoverAsync(IList? projectPaths = null, bool? excludeHostAgents = null, CancellationToken cancellationToken = default) + { + var request = new AgentsDiscoverRequest { ProjectPaths = projectPaths, ExcludeHostAgents = excludeHostAgents }; + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.discover", [request], cancellationToken); + } +} + +/// Provides server-scoped Instructions APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerInstructionsApi +{ + private readonly JsonRpc _rpc; + + internal ServerInstructionsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Discovers instruction sources across user, repository, and plugin sources. + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + /// The to monitor for cancellation requests. The default is . + /// Instruction sources discovered across user, repository, and plugin sources. + public async Task DiscoverAsync(IList? projectPaths = null, bool? excludeHostInstructions = null, CancellationToken cancellationToken = default) + { + var request = new InstructionsDiscoverRequest { ProjectPaths = projectPaths, ExcludeHostInstructions = excludeHostInstructions }; + return await CopilotClient.InvokeRpcAsync(_rpc, "instructions.discover", [request], cancellationToken); + } +} + /// Provides server-scoped User APIs. public sealed class ServerUserApi { @@ -17200,7 +17323,7 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) /// Opaque runtime MCP reload configuration. Marked internal: an in-process runtime shape (reloadMcpServers throws over the wire). /// The to monitor for cancellation requests. The default is . /// MCP server startup filtering result. - public async Task ReloadWithConfigAsync(object config, CancellationToken cancellationToken = default) + internal async Task ReloadWithConfigAsync(object config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(config); _session.ThrowIfDisposed(); @@ -17268,7 +17391,7 @@ public async Task RemoveGitHubAsync(CancellationToken can /// Opaque runtime auth info for GitHub MCP configuration. Marked internal: an in-process runtime shape (configureGitHubMcp is a no-op over the wire). /// The to monitor for cancellation requests. The default is . /// Result of configuring GitHub MCP. - public async Task ConfigureGitHubAsync(object authInfo, CancellationToken cancellationToken = default) + internal async Task ConfigureGitHubAsync(object authInfo, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(authInfo); _session.ThrowIfDisposed(); @@ -17281,7 +17404,7 @@ public async Task ConfigureGitHubAsync(object authInfo /// Name of the MCP server to start. /// Opaque server configuration (MCPServerConfig). Marked internal: an in-process runtime shape supplied only by in-process CLI callers. /// The to monitor for cancellation requests. The default is . - public async Task StartServerAsync(string serverName, object config, CancellationToken cancellationToken = default) + internal async Task StartServerAsync(string serverName, object config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(serverName); ArgumentNullException.ThrowIfNull(config); @@ -17295,7 +17418,7 @@ public async Task StartServerAsync(string serverName, object config, Cancellatio /// Name of the MCP server to restart. /// Opaque server configuration (MCPServerConfig). Marked internal: an in-process runtime shape supplied only by in-process CLI callers. /// The to monitor for cancellation requests. The default is . - public async Task RestartServerAsync(string serverName, object config, CancellationToken cancellationToken = default) + internal async Task RestartServerAsync(string serverName, object config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(serverName); ArgumentNullException.ThrowIfNull(config); @@ -17323,7 +17446,7 @@ public async Task StopServerAsync(string serverName, CancellationToken cancellat /// In-process MCP Transport instance. Marked internal: cannot be serialized across the JSON-RPC boundary. /// In-process server config (MCPServerConfig) paired with the in-process client/transport. Marked internal alongside its companions. /// The to monitor for cancellation requests. The default is . - public async Task RegisterExternalClientAsync(string serverName, object client, object transport, object config, CancellationToken cancellationToken = default) + internal async Task RegisterExternalClientAsync(string serverName, object client, object transport, object config, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(serverName); ArgumentNullException.ThrowIfNull(client); @@ -17338,7 +17461,7 @@ public async Task RegisterExternalClientAsync(string serverName, object client, /// Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime. /// Server name of the external client to unregister. /// The to monitor for cancellation requests. The default is . - public async Task UnregisterExternalClientAsync(string serverName, CancellationToken cancellationToken = default) + internal async Task UnregisterExternalClientAsync(string serverName, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(serverName); _session.ThrowIfDisposed(); @@ -17389,7 +17512,7 @@ internal McpOauthApi(CopilotSession session) /// In-process OAuthClientProvider instance, or omitted to deny. Marked internal: cannot be serialized across the JSON-RPC boundary. /// The to monitor for cancellation requests. The default is . /// Empty result after recording the MCP OAuth response. - public async Task RespondAsync(string requestId, object? provider = null, CancellationToken cancellationToken = default) + internal async Task RespondAsync(string requestId, object? provider = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(requestId); _session.ThrowIfDisposed(); @@ -19304,6 +19427,7 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncOn-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("eventsFileSizeBytes")] + public long? EventsFileSizeBytes { get; set; } + /// Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max"). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningEffort")] @@ -1508,6 +1513,16 @@ public sealed partial class SessionTitleChangedData /// Scheduled prompt registered via /every or /after. public sealed partial class SessionScheduleCreatedData { + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("at")] + public long? At { get; set; } + + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cron")] + public string? Cron { get; set; } + /// Optional user-facing label shown in the timeline instead of the actual prompt (e.g. `/skill-name args` when the prompt is a skill invocation expansion). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("displayPrompt")] @@ -1517,10 +1532,11 @@ public sealed partial class SessionScheduleCreatedData [JsonPropertyName("id")] public required long Id { get; set; } - /// Interval between ticks in milliseconds. + /// Interval between ticks in milliseconds (relative-interval schedules). [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("intervalMs")] - public required TimeSpan Interval { get; set; } + public TimeSpan? Interval { get; set; } /// Prompt text that gets enqueued on every tick. [JsonPropertyName("prompt")] @@ -1530,6 +1546,11 @@ public sealed partial class SessionScheduleCreatedData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("recurring")] public bool? Recurring { get; set; } + + /// IANA timezone the `cron` expression is evaluated in. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tz")] + public string? Tz { get; set; } } /// Scheduled prompt cancelled from the schedule manager dialog. @@ -1796,6 +1817,11 @@ public sealed partial class SessionShutdownData [JsonPropertyName("errorReason")] public string? ErrorReason { get; set; } + /// On-disk byte size of the session's persisted events.jsonl file at shutdown time; omitted when the file does not exist or cannot be stat'd. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("eventsFileSizeBytes")] + public long? EventsFileSizeBytes { get; set; } + /// Per-model usage breakdown, keyed by model identifier. [JsonPropertyName("modelMetrics")] public required IDictionary ModelMetrics { get; set; } @@ -2819,6 +2845,11 @@ public sealed partial class HookProgressData /// Human-readable progress message from the hook process. [JsonPropertyName("message")] public required string Message { get; set; } + + /// When true, this status message replaces the previous temporary one instead of accumulating. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("temporary")] + public bool? Temporary { get; set; } } /// System/developer instruction content with role and optional template metadata. diff --git a/dotnet/test/E2E/RpcMcpLifecycleE2ETests.cs b/dotnet/test/E2E/RpcMcpLifecycleE2ETests.cs index b87f7a478..60c4bde3f 100644 --- a/dotnet/test/E2E/RpcMcpLifecycleE2ETests.cs +++ b/dotnet/test/E2E/RpcMcpLifecycleE2ETests.cs @@ -9,9 +9,8 @@ namespace GitHub.Copilot.Test.E2E; /// -/// E2E coverage for the session-scoped MCP lifecycle RPC methods that were previously untested: -/// listTools, isServerRunning, stopServer, startServer, restartServer, registerExternalClient, -/// unregisterExternalClient, reloadWithConfig, configureGitHub, and oauth.respond. +/// E2E coverage for the public session-scoped MCP lifecycle RPC methods: +/// listTools, isServerRunning, and stopServer. /// public class RpcMcpLifecycleE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "rpc_mcp_lifecycle", output) @@ -71,121 +70,6 @@ public async Task Should_Stop_Running_Mcp_Server() await WaitForMcpRunningAsync(session, serverName, expectedRunning: false); } - [Fact] - public async Task Should_Start_And_Restart_Mcp_Server() - { - const string hostServer = "rpc-lifecycle-host-server"; - await using var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateTestMcpServers(hostServer), - }); - await WaitForMcpServerStatusAsync(session, hostServer, McpServerStatus.Connected); - - // Start a brand-new server through the lifecycle API, reusing the exact stdio config shape - // the bulk session-config path uses so the runtime accepts and connects it. - const string startedServer = "rpc-lifecycle-started-server"; - var config = CreateTestMcpServers(startedServer)[startedServer]; - - await session.Rpc.Mcp.StartServerAsync(startedServer, config); - await WaitForMcpRunningAsync(session, startedServer, expectedRunning: true); - - // The freshly started server exposes its tools just like a config-provided server. - var tools = await session.Rpc.Mcp.ListToolsAsync(startedServer); - Assert.NotEmpty(tools.Tools); - - // Restart stops then starts the same server; it must end up running again. - await session.Rpc.Mcp.RestartServerAsync(startedServer, config); - await WaitForMcpRunningAsync(session, startedServer, expectedRunning: true); - } - - [Fact] - public async Task Should_Register_And_Unregister_External_Mcp_Client() - { - const string hostServer = "rpc-lifecycle-extclient-host"; - await using var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateTestMcpServers(hostServer), - }); - await WaitForMcpServerStatusAsync(session, hostServer, McpServerStatus.Connected); - - const string externalName = "rpc-lifecycle-external-client"; - Assert.False((await session.Rpc.Mcp.IsServerRunningAsync(externalName)).Running); - - // The runtime stores the supplied client/transport handles on the host registry, so the - // registered name immediately reports as running until it is unregistered again. - await session.Rpc.Mcp.RegisterExternalClientAsync( - externalName, - client: new Dictionary { ["id"] = externalName }, - transport: new Dictionary { ["kind"] = "in-process" }, - config: new Dictionary { ["command"] = "noop" }); - Assert.True((await session.Rpc.Mcp.IsServerRunningAsync(externalName)).Running); - - await session.Rpc.Mcp.UnregisterExternalClientAsync(externalName); - Assert.False((await session.Rpc.Mcp.IsServerRunningAsync(externalName)).Running); - } - - [Fact] - public async Task Should_Reload_Mcp_Servers_With_Config() - { - const string hostServer = "rpc-lifecycle-reload-host"; - await using var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateTestMcpServers(hostServer), - }); - await WaitForMcpServerStatusAsync(session, hostServer, McpServerStatus.Connected); - - // reloadWithConfig drives the runtime's reloadMcpServers with an explicit host config and - // returns the startup filtering result. Reloading an empty server set is a valid no-op. - var result = await session.Rpc.Mcp.ReloadWithConfigAsync(new Dictionary - { - ["mcpServers"] = new Dictionary(), - ["disabledServers"] = new List(), - }); - - Assert.NotNull(result); - Assert.NotNull(result.FilteredServers); - Assert.Empty(result.FilteredServers); - } - - [Fact] - public async Task Should_Configure_GitHub_Mcp_Server() - { - const string hostServer = "rpc-lifecycle-configure-host"; - await using var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateTestMcpServers(hostServer), - }); - await WaitForMcpServerStatusAsync(session, hostServer, McpServerStatus.Connected); - - // configureGitHub forwards a typed auth-info union to the runtime. An "api-key" auth info is - // a recognized type that the runtime declines to act on, so configuration is left unchanged - // (changed=false) while still proving the method is wired through to the handler. - var result = await session.Rpc.Mcp.ConfigureGitHubAsync(new Dictionary - { - ["type"] = "api-key", - }); - - Assert.NotNull(result); - Assert.False(result.Changed); - } - - [Fact] - public async Task Should_Respond_To_Mcp_Oauth_Request_Without_Pending_Request() - { - const string hostServer = "rpc-lifecycle-oauth-host"; - await using var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateTestMcpServers(hostServer), - }); - await WaitForMcpServerStatusAsync(session, hostServer, McpServerStatus.Connected); - - // With no pending OAuth request, the runtime's respondToMcpOAuth is a tolerant no-op: it - // looks up the request id, finds nothing, and returns an empty result without throwing. The - // call must reach the runtime and complete successfully, proving the method is wired. - var result = await session.Rpc.Mcp.Oauth.RespondAsync($"missing-{Guid.NewGuid():N}"); - Assert.NotNull(result); - } - private static Task WaitForMcpRunningAsync(CopilotSession session, string serverName, bool expectedRunning) => Harness.TestHelper.WaitForConditionAsync( async () => (await session.Rpc.Mcp.IsServerRunningAsync(serverName)).Running == expectedRunning, diff --git a/go/internal/e2e/rpc_mcp_lifecycle_e2e_test.go b/go/internal/e2e/rpc_mcp_lifecycle_e2e_test.go index 36bd5f5c3..cfe1123fd 100644 --- a/go/internal/e2e/rpc_mcp_lifecycle_e2e_test.go +++ b/go/internal/e2e/rpc_mcp_lifecycle_e2e_test.go @@ -80,126 +80,6 @@ func TestRpcMcpLifecycle(t *testing.T) { } waitForPortedMCPRunning(t, session, serverName, false) }) - - t.Run("should_start_and_restart_mcp_server", func(t *testing.T) { - ctx.ConfigureForTest(t) - const hostServer = "rpc-lifecycle-host-server" - session := createPortedSession(t, client, &copilot.SessionConfig{MCPServers: testMCPServers(t, hostServer)}) - defer session.Disconnect() - waitForPortedMCPServerStatus(t, session, hostServer, rpc.MCPServerStatusConnected) - - const startedServer = "rpc-lifecycle-started-server" - config := testMCPServers(t, startedServer)[startedServer] - if _, err := session.RPC.MCP.StartServer(t.Context(), &rpc.MCPStartServerRequest{ServerName: startedServer, Config: config}); err != nil { - t.Fatalf("MCP.StartServer failed: %v", err) - } - waitForPortedMCPRunning(t, session, startedServer, true) - - tools, err := session.RPC.MCP.ListTools(t.Context(), &rpc.MCPListToolsRequest{ServerName: startedServer}) - if err != nil { - t.Fatalf("MCP.ListTools(started) failed: %v", err) - } - if len(tools.Tools) == 0 { - t.Fatal("Expected started MCP server to expose tools") - } - - if _, err := session.RPC.MCP.RestartServer(t.Context(), &rpc.MCPRestartServerRequest{ServerName: startedServer, Config: config}); err != nil { - t.Fatalf("MCP.RestartServer failed: %v", err) - } - waitForPortedMCPRunning(t, session, startedServer, true) - }) - - t.Run("should_register_and_unregister_external_mcp_client", func(t *testing.T) { - ctx.ConfigureForTest(t) - const hostServer = "rpc-lifecycle-extclient-host" - session := createPortedSession(t, client, &copilot.SessionConfig{MCPServers: testMCPServers(t, hostServer)}) - defer session.Disconnect() - waitForPortedMCPServerStatus(t, session, hostServer, rpc.MCPServerStatusConnected) - - const externalName = "rpc-lifecycle-external-client" - initial, err := session.RPC.MCP.IsServerRunning(t.Context(), &rpc.MCPIsServerRunningRequest{ServerName: externalName}) - if err != nil { - t.Fatalf("MCP.IsServerRunning(initial external) failed: %v", err) - } - if initial.Running { - t.Fatal("Expected external client to start as not running") - } - - if _, err := session.RPC.MCP.RegisterExternalClient(t.Context(), &rpc.MCPRegisterExternalClientRequest{ - ServerName: externalName, - Client: map[string]any{"id": externalName}, - Transport: map[string]any{"kind": "in-process"}, - Config: map[string]any{"command": "noop"}, - }); err != nil { - t.Fatalf("MCP.RegisterExternalClient failed: %v", err) - } - waitForPortedMCPRunning(t, session, externalName, true) - - if _, err := session.RPC.MCP.UnregisterExternalClient(t.Context(), &rpc.MCPUnregisterExternalClientRequest{ServerName: externalName}); err != nil { - t.Fatalf("MCP.UnregisterExternalClient failed: %v", err) - } - waitForPortedMCPRunning(t, session, externalName, false) - }) - - t.Run("should_reload_mcp_servers_with_config", func(t *testing.T) { - ctx.ConfigureForTest(t) - const hostServer = "rpc-lifecycle-reload-host" - session := createPortedSession(t, client, &copilot.SessionConfig{MCPServers: testMCPServers(t, hostServer)}) - defer session.Disconnect() - waitForPortedMCPServerStatus(t, session, hostServer, rpc.MCPServerStatusConnected) - - result, err := session.RPC.MCP.ReloadWithConfig(t.Context(), &rpc.MCPReloadWithConfigRequest{Config: map[string]any{ - "mcpServers": map[string]any{}, - "disabledServers": []string{}, - }}) - if err != nil { - t.Fatalf("MCP.ReloadWithConfig failed: %v", err) - } - if result == nil { - t.Fatal("Expected non-nil reload result") - } - if result.FilteredServers == nil { - t.Fatal("Expected non-nil FilteredServers") - } - if len(result.FilteredServers) != 0 { - t.Fatalf("Expected no filtered servers, got %+v", result.FilteredServers) - } - }) - - t.Run("should_configure_github_mcp_server", func(t *testing.T) { - ctx.ConfigureForTest(t) - const hostServer = "rpc-lifecycle-configure-host" - session := createPortedSession(t, client, &copilot.SessionConfig{MCPServers: testMCPServers(t, hostServer)}) - defer session.Disconnect() - waitForPortedMCPServerStatus(t, session, hostServer, rpc.MCPServerStatusConnected) - - result, err := session.RPC.MCP.ConfigureGitHub(t.Context(), &rpc.MCPConfigureGitHubRequest{AuthInfo: map[string]any{"type": "api-key"}}) - if err != nil { - t.Fatalf("MCP.ConfigureGitHub failed: %v", err) - } - if result == nil { - t.Fatal("Expected non-nil configure result") - } - if result.Changed { - t.Fatal("Expected Changed=false") - } - }) - - t.Run("should_respond_to_mcp_oauth_request_without_pending_request", func(t *testing.T) { - ctx.ConfigureForTest(t) - const hostServer = "rpc-lifecycle-oauth-host" - session := createPortedSession(t, client, &copilot.SessionConfig{MCPServers: testMCPServers(t, hostServer)}) - defer session.Disconnect() - waitForPortedMCPServerStatus(t, session, hostServer, rpc.MCPServerStatusConnected) - - result, err := session.RPC.MCP.Oauth().Respond(t.Context(), &rpc.MCPOauthRespondRequest{RequestID: "missing-" + randomHex(t)}) - if err != nil { - t.Fatalf("MCP.Oauth.Respond failed: %v", err) - } - if result == nil { - t.Fatal("Expected non-nil OAuth respond result") - } - }) } func waitForPortedMCPServerStatus(t *testing.T, session *copilot.Session, serverName string, expectedStatus rpc.MCPServerStatus) { diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 6c3252007..b1d9df103 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -294,6 +294,18 @@ type AgentReloadResult struct { Agents []AgentInfo `json:"agents"` } +// Optional project paths to include in agent discovery. +// Experimental: AgentsDiscoverRequest is part of an experimental API and may change or be +// removed. +type AgentsDiscoverRequest struct { + // When true, omit the host's agents (the `/agents` directory and all plugin + // agents), leaving only project and remote agents. For multitenant deployments. + ExcludeHostAgents *bool `json:"excludeHostAgents,omitempty"` + // Optional list of project directory paths to scan for project-scoped agents. When omitted + // or empty, only user/plugin/remote-independent agents are returned (no project scan). + ProjectPaths []string `json:"projectPaths,omitzero"` +} + // Name of the custom agent to select for subsequent turns. // Experimental: AgentSelectRequest is part of an experimental API and may change or be // removed. @@ -1772,18 +1784,31 @@ type InstalledPluginSourceURL struct { URL string `json:"url"` } +// Optional project paths to include in instruction discovery. +// Experimental: InstructionsDiscoverRequest is part of an experimental API and may change +// or be removed. +type InstructionsDiscoverRequest struct { + // When true, omit the host's instruction sources (user/home-level files and plugin rules), + // leaving only repository and working-directory sources. For multitenant deployments. + ExcludeHostInstructions *bool `json:"excludeHostInstructions,omitempty"` + // Optional list of project directory paths to scan for repository/working-directory + // instruction sources. When omitted or empty, only user-level and plugin instruction + // sources are returned (no project scan). + ProjectPaths []string `json:"projectPaths,omitzero"` +} + // Instruction sources loaded for the session, in merge order. // Experimental: InstructionsGetSourcesResult is part of an experimental API and may change // or be removed. type InstructionsGetSourcesResult struct { // Instruction sources for the session - Sources []InstructionsSources `json:"sources"` + Sources []InstructionSource `json:"sources"` } -// Schema for the `InstructionsSources` type. -// Experimental: InstructionsSources is part of an experimental API and may change or be +// Schema for the `InstructionSource` type. +// Experimental: InstructionSource is part of an experimental API and may change or be // removed. -type InstructionsSources struct { +type InstructionSource struct { // Glob pattern(s) from frontmatter — when set, this instruction applies only to matching // files ApplyTo []string `json:"applyTo,omitzero"` @@ -1798,11 +1823,16 @@ type InstructionsSources struct { // Human-readable label Label string `json:"label"` // Where this source lives — used for UI grouping - Location InstructionsSourcesLocation `json:"location"` + Location InstructionSourceLocation `json:"location"` + // The project path this source was discovered from. Only set by sessionless discovery for + // repository/working-directory sources, where it disambiguates same-named files (e.g. + // .github/copilot-instructions.md) across multiple workspace roots. The session-scoped + // getSources leaves it unset. + ProjectPath *string `json:"projectPath,omitempty"` // File path relative to repo or absolute for home SourcePath string `json:"sourcePath"` // Category of instruction source — used for merge logic - Type InstructionsSourcesType `json:"type"` + Type InstructionSourceType `json:"type"` } // Schema for the `LocalSessionMetadataValue` type. @@ -5106,20 +5136,26 @@ type SandboxConfigUserPolicyNetwork struct { // Schema for the `ScheduleEntry` type. // Experimental: ScheduleEntry is part of an experimental API and may change or be removed. type ScheduleEntry struct { + // Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + At *int64 `json:"at,omitempty"` + // 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + Cron *string `json:"cron,omitempty"` // Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a // skill-invocation schedule). The actual enqueued prompt is `prompt`. DisplayPrompt *string `json:"displayPrompt,omitempty"` // Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt // from the event log). ID int64 `json:"id"` - // Interval between scheduled ticks, in milliseconds. - IntervalMs int64 `json:"intervalMs"` + // Interval between scheduled ticks, in milliseconds (relative-interval schedules). + IntervalMs *int64 `json:"intervalMs,omitempty"` // ISO 8601 timestamp when the next tick is scheduled to fire. NextRunAt time.Time `json:"nextRunAt"` // Prompt text that gets enqueued on every tick. Prompt string `json:"prompt"` // Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). Recurring bool `json:"recurring"` + // IANA timezone the `cron` expression is evaluated in. + Tz *string `json:"tz,omitempty"` } // Snapshot of the currently active recurring prompts for this session. @@ -5223,6 +5259,21 @@ type SendResult struct { MessageID string `json:"messageId"` } +// Agents discovered across user, project, plugin, and remote sources. +// Experimental: ServerAgentList is part of an experimental API and may change or be removed. +type ServerAgentList struct { + // All discovered agents across all sources + Agents []AgentInfo `json:"agents"` +} + +// Instruction sources discovered across user, repository, and plugin sources. +// Experimental: ServerInstructionSourceList is part of an experimental API and may change +// or be removed. +type ServerInstructionSourceList struct { + // All discovered instruction sources + Sources []InstructionSource `json:"sources"` +} + // Schema for the `ServerSkill` type. type ServerSkill struct { // Description of what the skill does @@ -7204,11 +7255,13 @@ type TaskAgentInfo struct { IdleSince *time.Time `json:"idleSince,omitempty"` // Most recent response text from the agent LatestResponse *string `json:"latestResponse,omitempty"` - // Model used for the task when specified + // Requested model override for the task when specified Model *string `json:"model,omitempty"` // Most recent prompt delivered to the agent. Updated whenever the agent receives a // follow-up message. Prompt string `json:"prompt"` + // Runtime model resolved for the task when available + ResolvedModel *string `json:"resolvedModel,omitempty"` // Result text from the task when available Result *string `json:"result,omitempty"` // ISO 8601 timestamp when the task was started @@ -8797,41 +8850,41 @@ const ( ) // Where this source lives — used for UI grouping -// Experimental: InstructionsSourcesLocation is part of an experimental API and may change -// or be removed. -type InstructionsSourcesLocation string +// Experimental: InstructionSourceLocation is part of an experimental API and may change or +// be removed. +type InstructionSourceLocation string const ( // Instructions live in plugin-provided configuration. - InstructionsSourcesLocationPlugin InstructionsSourcesLocation = "plugin" + InstructionSourceLocationPlugin InstructionSourceLocation = "plugin" // Instructions live in repository-level configuration. - InstructionsSourcesLocationRepository InstructionsSourcesLocation = "repository" + InstructionSourceLocationRepository InstructionSourceLocation = "repository" // Instructions live in user-level configuration. - InstructionsSourcesLocationUser InstructionsSourcesLocation = "user" + InstructionSourceLocationUser InstructionSourceLocation = "user" // Instructions live under the current working directory. - InstructionsSourcesLocationWorkingDirectory InstructionsSourcesLocation = "working-directory" + InstructionSourceLocationWorkingDirectory InstructionSourceLocation = "working-directory" ) // Category of instruction source — used for merge logic -// Experimental: InstructionsSourcesType is part of an experimental API and may change or be +// Experimental: InstructionSourceType is part of an experimental API and may change or be // removed. -type InstructionsSourcesType string +type InstructionSourceType string const ( // Instructions inherited from child instruction files. - InstructionsSourcesTypeChildInstructions InstructionsSourcesType = "child-instructions" + InstructionSourceTypeChildInstructions InstructionSourceType = "child-instructions" // Instructions loaded from the user's home configuration. - InstructionsSourcesTypeHome InstructionsSourcesType = "home" + InstructionSourceTypeHome InstructionSourceType = "home" // Instructions loaded from model-specific files. - InstructionsSourcesTypeModel InstructionsSourcesType = "model" + InstructionSourceTypeModel InstructionSourceType = "model" // Instructions discovered from nested agent files. - InstructionsSourcesTypeNestedAgents InstructionsSourcesType = "nested-agents" + InstructionSourceTypeNestedAgents InstructionSourceType = "nested-agents" // Instructions supplied by an installed plugin. - InstructionsSourcesTypePlugin InstructionsSourcesType = "plugin" + InstructionSourceTypePlugin InstructionSourceType = "plugin" // Instructions loaded from repository-scoped files. - InstructionsSourcesTypeRepo InstructionsSourcesType = "repo" + InstructionSourceTypeRepo InstructionSourceType = "repo" // Instructions loaded from VS Code instruction files. - InstructionsSourcesTypeVscode InstructionsSourcesType = "vscode" + InstructionSourceTypeVscode InstructionSourceType = "vscode" ) // Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. @@ -10074,6 +10127,51 @@ func (a *ServerAgentRegistryAPI) Spawn(ctx context.Context, params *AgentRegistr return result, nil } +// Experimental: ServerAgentsAPI contains experimental APIs that may change or be removed. +type ServerAgentsAPI serverAPI + +// Discovers custom agents across user, project, plugin, and remote sources. +// +// RPC method: agents.discover. +// +// Parameters: Optional project paths to include in agent discovery. +// +// Returns: Agents discovered across user, project, plugin, and remote sources. +func (a *ServerAgentsAPI) Discover(ctx context.Context, params *AgentsDiscoverRequest) (*ServerAgentList, error) { + raw, err := a.client.Request("agents.discover", params) + if err != nil { + return nil, err + } + var result ServerAgentList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: ServerInstructionsAPI contains experimental APIs that may change or be +// removed. +type ServerInstructionsAPI serverAPI + +// Discovers instruction sources across user, repository, and plugin sources. +// +// RPC method: instructions.discover. +// +// Parameters: Optional project paths to include in instruction discovery. +// +// Returns: Instruction sources discovered across user, repository, and plugin sources. +func (a *ServerInstructionsAPI) Discover(ctx context.Context, params *InstructionsDiscoverRequest) (*ServerInstructionSourceList, error) { + raw, err := a.client.Request("instructions.discover", params) + if err != nil { + return nil, err + } + var result ServerInstructionSourceList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + type ServerMCPAPI serverAPI // Discovers MCP servers from user, workspace, plugin, and builtin sources. @@ -11114,6 +11212,8 @@ type ServerRPC struct { Account *ServerAccountAPI AgentRegistry *ServerAgentRegistryAPI + Agents *ServerAgentsAPI + Instructions *ServerInstructionsAPI MCP *ServerMCPAPI Models *ServerModelsAPI Plugins *ServerPluginsAPI @@ -11151,6 +11251,8 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.common = serverAPI{client: client} r.Account = (*ServerAccountAPI)(&r.common) r.AgentRegistry = (*ServerAgentRegistryAPI)(&r.common) + r.Agents = (*ServerAgentsAPI)(&r.common) + r.Instructions = (*ServerInstructionsAPI)(&r.common) r.MCP = (*ServerMCPAPI)(&r.common) r.Models = (*ServerModelsAPI)(&r.common) r.Plugins = (*ServerPluginsAPI)(&r.common) @@ -12245,30 +12347,6 @@ func (a *MCPAPI) CancelSamplingExecution(ctx context.Context, params *MCPCancelS return &result, nil } -// ConfigureGitHub configures the built-in GitHub MCP server for the session's current auth -// context. -// -// RPC method: session.mcp.configureGitHub. -// -// Parameters: Opaque auth info used to configure GitHub MCP. -// -// Returns: Result of configuring GitHub MCP. -func (a *MCPAPI) ConfigureGitHub(ctx context.Context, params *MCPConfigureGitHubRequest) (*MCPConfigureGitHubResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["authInfo"] = params.AuthInfo - } - raw, err := a.client.Request("session.mcp.configureGitHub", req) - if err != nil { - return nil, err - } - var result MCPConfigureGitHubResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // Disables an MCP server for the session. // // RPC method: session.mcp.disable. @@ -12409,35 +12487,6 @@ func (a *MCPAPI) ListTools(ctx context.Context, params *MCPListToolsRequest) (*M return &result, nil } -// RegisterExternalClient registers a pre-connected external MCP client (e.g. IDE) on the -// session's host. The caller retains lifecycle ownership of the client and transport. -// Marked internal because the `client` and `transport` arguments are in-process MCP SDK -// instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on -// top of the SDK, external clients will be expressed as transport configs the runtime can -// construct itself. -// -// RPC method: session.mcp.registerExternalClient. -// -// Parameters: Registration parameters for an external MCP client. -func (a *MCPAPI) RegisterExternalClient(ctx context.Context, params *MCPRegisterExternalClientRequest) (*SessionMCPRegisterExternalClientResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["client"] = params.Client - req["config"] = params.Config - req["serverName"] = params.ServerName - req["transport"] = params.Transport - } - raw, err := a.client.Request("session.mcp.registerExternalClient", req) - if err != nil { - return nil, err - } - var result SessionMCPRegisterExternalClientResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // Reloads MCP server connections for the session. // // RPC method: session.mcp.reload. @@ -12454,30 +12503,6 @@ func (a *MCPAPI) Reload(ctx context.Context) (*SessionMCPReloadResult, error) { return &result, nil } -// ReloadWithConfig reloads MCP server connections for the session with an explicit -// host-provided configuration. -// -// RPC method: session.mcp.reloadWithConfig. -// -// Parameters: Opaque MCP reload configuration. -// -// Returns: MCP server startup filtering result. -func (a *MCPAPI) ReloadWithConfig(ctx context.Context, params *MCPReloadWithConfigRequest) (*MCPStartServersResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["config"] = params.Config - } - raw, err := a.client.Request("session.mcp.reloadWithConfig", req) - if err != nil { - return nil, err - } - var result MCPStartServersResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // RemoveGitHub removes the auto-managed `github` MCP server when present. // // RPC method: session.mcp.removeGitHub. @@ -12497,28 +12522,6 @@ func (a *MCPAPI) RemoveGitHub(ctx context.Context) (*MCPRemoveGitHubResult, erro return &result, nil } -// RestartServer restarts an individual MCP server on the session's host (stops then starts). -// -// RPC method: session.mcp.restartServer. -// -// Parameters: Server name and opaque configuration for an individual MCP server restart. -func (a *MCPAPI) RestartServer(ctx context.Context, params *MCPRestartServerRequest) (*SessionMCPRestartServerResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["config"] = params.Config - req["serverName"] = params.ServerName - } - raw, err := a.client.Request("session.mcp.restartServer", req) - if err != nil { - return nil, err - } - var result SessionMCPRestartServerResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // SetEnvValueMode sets how environment-variable values supplied to MCP servers are resolved // (direct or indirect). // @@ -12544,28 +12547,6 @@ func (a *MCPAPI) SetEnvValueMode(ctx context.Context, params *MCPSetEnvValueMode return &result, nil } -// StartServer starts an individual MCP server on the session's host. -// -// RPC method: session.mcp.startServer. -// -// Parameters: Server name and opaque configuration for an individual MCP server start. -func (a *MCPAPI) StartServer(ctx context.Context, params *MCPStartServerRequest) (*SessionMCPStartServerResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["config"] = params.Config - req["serverName"] = params.ServerName - } - raw, err := a.client.Request("session.mcp.startServer", req) - if err != nil { - return nil, err - } - var result SessionMCPStartServerResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // StopServer stops an individual MCP server on the session's host. // // RPC method: session.mcp.stopServer. @@ -12587,32 +12568,6 @@ func (a *MCPAPI) StopServer(ctx context.Context, params *MCPStopServerRequest) ( return &result, nil } -// UnregisterExternalClient unregisters a previously registered external MCP client by -// server name. Marked internal as the paired companion of `registerExternalClient`: only -// in-process callers that registered a client this way can meaningfully unregister it. -// Disappears alongside `registerExternalClient`: once external clients are described to the -// runtime as config rather than handed in as instances, lifecycle (including -// deregistration) is owned entirely by the runtime. -// -// RPC method: session.mcp.unregisterExternalClient. -// -// Parameters: Server name identifying the external client to remove. -func (a *MCPAPI) UnregisterExternalClient(ctx context.Context, params *MCPUnregisterExternalClientRequest) (*SessionMCPUnregisterExternalClientResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - req["serverName"] = params.ServerName - } - raw, err := a.client.Request("session.mcp.unregisterExternalClient", req) - if err != nil { - return nil, err - } - var result SessionMCPUnregisterExternalClientResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // Experimental: MCPAppsAPI contains experimental APIs that may change or be removed. type MCPAppsAPI sessionAPI @@ -12803,35 +12758,6 @@ func (a *MCPOauthAPI) Login(ctx context.Context, params *MCPOauthLoginRequest) ( return &result, nil } -// Responds to a pending MCP OAuth provider request. Marked internal because the `provider` -// argument is an in-process OAuthClientProvider instance that cannot be carried over the -// wire; the public OAuth surface will route the response through a wire-clean handshake -// once the CLI moves on top of the SDK. -// -// RPC method: session.mcp.oauth.respond. -// -// Parameters: MCP OAuth request id and optional provider response. -// -// Returns: Empty result after recording the MCP OAuth response. -func (a *MCPOauthAPI) Respond(ctx context.Context, params *MCPOauthRespondRequest) (*MCPOauthRespondResult, error) { - req := map[string]any{"sessionId": a.sessionID} - if params != nil { - if params.Provider != nil { - req["provider"] = params.Provider - } - req["requestId"] = params.RequestID - } - raw, err := a.client.Request("session.mcp.oauth.respond", req) - if err != nil { - return nil, err - } - var result MCPOauthRespondResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // Experimental: Oauth returns experimental APIs that may change or be removed. func (s *MCPAPI) Oauth() *MCPOauthAPI { return (*MCPOauthAPI)(s) @@ -15549,6 +15475,229 @@ func NewSessionRPC(client *jsonrpc2.Client, sessionID string) *SessionRPC { return r } +type internalSessionAPI struct { + client *jsonrpc2.Client + sessionID string +} + +// Experimental: InternalMCPAPI contains experimental APIs that may change or be removed. +type InternalMCPAPI internalSessionAPI + +// ConfigureGitHub configures the built-in GitHub MCP server for the session's current auth +// context. +// +// RPC method: session.mcp.configureGitHub. +// +// Parameters: Opaque auth info used to configure GitHub MCP. +// +// Returns: Result of configuring GitHub MCP. +// Internal: ConfigureGitHub is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalMCPAPI) ConfigureGitHub(ctx context.Context, params *MCPConfigureGitHubRequest) (*MCPConfigureGitHubResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["authInfo"] = params.AuthInfo + } + raw, err := a.client.Request("session.mcp.configureGitHub", req) + if err != nil { + return nil, err + } + var result MCPConfigureGitHubResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// RegisterExternalClient registers a pre-connected external MCP client (e.g. IDE) on the +// session's host. The caller retains lifecycle ownership of the client and transport. +// Marked internal because the `client` and `transport` arguments are in-process MCP SDK +// instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on +// top of the SDK, external clients will be expressed as transport configs the runtime can +// construct itself. +// +// RPC method: session.mcp.registerExternalClient. +// +// Parameters: Registration parameters for an external MCP client. +// Internal: RegisterExternalClient is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalMCPAPI) RegisterExternalClient(ctx context.Context, params *MCPRegisterExternalClientRequest) (*SessionMCPRegisterExternalClientResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["client"] = params.Client + req["config"] = params.Config + req["serverName"] = params.ServerName + req["transport"] = params.Transport + } + raw, err := a.client.Request("session.mcp.registerExternalClient", req) + if err != nil { + return nil, err + } + var result SessionMCPRegisterExternalClientResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ReloadWithConfig reloads MCP server connections for the session with an explicit +// host-provided configuration. +// +// RPC method: session.mcp.reloadWithConfig. +// +// Parameters: Opaque MCP reload configuration. +// +// Returns: MCP server startup filtering result. +// Internal: ReloadWithConfig is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalMCPAPI) ReloadWithConfig(ctx context.Context, params *MCPReloadWithConfigRequest) (*MCPStartServersResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["config"] = params.Config + } + raw, err := a.client.Request("session.mcp.reloadWithConfig", req) + if err != nil { + return nil, err + } + var result MCPStartServersResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// RestartServer restarts an individual MCP server on the session's host (stops then starts). +// +// RPC method: session.mcp.restartServer. +// +// Parameters: Server name and opaque configuration for an individual MCP server restart. +// Internal: RestartServer is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalMCPAPI) RestartServer(ctx context.Context, params *MCPRestartServerRequest) (*SessionMCPRestartServerResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["config"] = params.Config + req["serverName"] = params.ServerName + } + raw, err := a.client.Request("session.mcp.restartServer", req) + if err != nil { + return nil, err + } + var result SessionMCPRestartServerResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// StartServer starts an individual MCP server on the session's host. +// +// RPC method: session.mcp.startServer. +// +// Parameters: Server name and opaque configuration for an individual MCP server start. +// Internal: StartServer is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalMCPAPI) StartServer(ctx context.Context, params *MCPStartServerRequest) (*SessionMCPStartServerResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["config"] = params.Config + req["serverName"] = params.ServerName + } + raw, err := a.client.Request("session.mcp.startServer", req) + if err != nil { + return nil, err + } + var result SessionMCPStartServerResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// UnregisterExternalClient unregisters a previously registered external MCP client by +// server name. Marked internal as the paired companion of `registerExternalClient`: only +// in-process callers that registered a client this way can meaningfully unregister it. +// Disappears alongside `registerExternalClient`: once external clients are described to the +// runtime as config rather than handed in as instances, lifecycle (including +// deregistration) is owned entirely by the runtime. +// +// RPC method: session.mcp.unregisterExternalClient. +// +// Parameters: Server name identifying the external client to remove. +// Internal: UnregisterExternalClient is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalMCPAPI) UnregisterExternalClient(ctx context.Context, params *MCPUnregisterExternalClientRequest) (*SessionMCPUnregisterExternalClientResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["serverName"] = params.ServerName + } + raw, err := a.client.Request("session.mcp.unregisterExternalClient", req) + if err != nil { + return nil, err + } + var result SessionMCPUnregisterExternalClientResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalMCPOauthAPI contains experimental APIs that may change or be +// removed. +type InternalMCPOauthAPI internalSessionAPI + +// Responds to a pending MCP OAuth provider request. Marked internal because the `provider` +// argument is an in-process OAuthClientProvider instance that cannot be carried over the +// wire; the public OAuth surface will route the response through a wire-clean handshake +// once the CLI moves on top of the SDK. +// +// RPC method: session.mcp.oauth.respond. +// +// Parameters: MCP OAuth request id and optional provider response. +// +// Returns: Empty result after recording the MCP OAuth response. +// Internal: Respond is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalMCPOauthAPI) Respond(ctx context.Context, params *MCPOauthRespondRequest) (*MCPOauthRespondResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.Provider != nil { + req["provider"] = params.Provider + } + req["requestId"] = params.RequestID + } + raw, err := a.client.Request("session.mcp.oauth.respond", req) + if err != nil { + return nil, err + } + var result MCPOauthRespondResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Oauth returns experimental APIs that may change or be removed. +func (s *InternalMCPAPI) Oauth() *InternalMCPOauthAPI { + return (*InternalMCPOauthAPI)(s) +} + +// InternalSessionRPC provides internal SDK session-scoped RPC methods (handshake helpers +// etc.). Not part of the public API. +type InternalSessionRPC struct { + // Reuse a single struct instead of allocating one for each service on the heap. + common internalSessionAPI + + MCP *InternalMCPAPI +} + +func NewInternalSessionRPC(client *jsonrpc2.Client, sessionID string) *InternalSessionRPC { + r := &InternalSessionRPC{} + r.common = internalSessionAPI{client: client, sessionID: sessionID} + r.MCP = (*InternalMCPAPI)(&r.common) + return r +} + // Experimental: CanvasHandler contains experimental APIs that may change or be removed. type CanvasHandler interface { // Closes a canvas instance on the provider. diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 5148d46c3..98d008256 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -431,6 +431,8 @@ func (*PendingMessagesModifiedData) Type() SessionEventType { type HookProgressData struct { // Human-readable progress message from the hook process Message string `json:"message"` + // When true, this status message replaces the previous temporary one instead of accumulating + Temporary *bool `json:"temporary,omitempty"` } func (*HookProgressData) sessionEventData() {} @@ -894,16 +896,22 @@ func (*SessionScheduleCancelledData) Type() SessionEventType { // Scheduled prompt registered via /every or /after type SessionScheduleCreatedData struct { + // Absolute fire time (epoch milliseconds) for a one-shot calendar schedule + At *int64 `json:"at,omitempty"` + // 5-field cron expression for a recurring calendar schedule, evaluated in `tz` + Cron *string `json:"cron,omitempty"` // Optional user-facing label shown in the timeline instead of the actual prompt (e.g. `/skill-name args` when the prompt is a skill invocation expansion) DisplayPrompt *string `json:"displayPrompt,omitempty"` // Sequential id assigned to the scheduled prompt within the session ID int64 `json:"id"` - // Interval between ticks in milliseconds - IntervalMs int64 `json:"intervalMs"` + // Interval between ticks in milliseconds (relative-interval schedules) + IntervalMs *int64 `json:"intervalMs,omitempty"` // Prompt text that gets enqueued on every tick Prompt string `json:"prompt"` // Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) Recurring *bool `json:"recurring,omitempty"` + // IANA timezone the `cron` expression is evaluated in + Tz *string `json:"tz,omitempty"` } func (*SessionScheduleCreatedData) sessionEventData() {} @@ -1154,6 +1162,8 @@ type SessionResumeData struct { ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` // Total number of persisted events in the session at the time of resume EventCount int64 `json:"eventCount"` + // On-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd + EventsFileSizeBytes *int64 `json:"eventsFileSizeBytes,omitempty"` // Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed") @@ -1196,6 +1206,8 @@ type SessionShutdownData struct { CurrentTokens *int64 `json:"currentTokens,omitempty"` // Error description when shutdownType is "error" ErrorReason *string `json:"errorReason,omitempty"` + // On-disk byte size of the session's persisted events.jsonl file at shutdown time; omitted when the file does not exist or cannot be stat'd + EventsFileSizeBytes *int64 `json:"eventsFileSizeBytes,omitempty"` // Per-model usage breakdown, keyed by model identifier ModelMetrics map[string]ShutdownModelMetric `json:"modelMetrics"` // Unix timestamp (milliseconds) when the session started diff --git a/java/pom.xml b/java/pom.xml index 125b9afe0..0281e4e92 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -86,7 +86,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.60 + ^1.0.61 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index e58973859..85f8d5745 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "json-schema": "^0.4.0", "tsx": "^4.20.6" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.60.tgz", - "integrity": "sha512-+GjW+GJNo55nwJwt48o9szWcyhuY0u682cBKQI1ay9jVBX8DCCXC6HB6Tyv5/MaM4N7CxTiEgp48aVMkye8K+g==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.61.tgz", + "integrity": "sha512-E4f7YXTL2uUZY/ypnfsUruAeSgrHx3AGYEbm5N0DrpzPqoNAZqV6kHEWM4vu+W/nGvydIfPxmOTqaMEhM8r0Uw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.60", - "@github/copilot-darwin-x64": "1.0.60", - "@github/copilot-linux-arm64": "1.0.60", - "@github/copilot-linux-x64": "1.0.60", - "@github/copilot-linuxmusl-arm64": "1.0.60", - "@github/copilot-linuxmusl-x64": "1.0.60", - "@github/copilot-win32-arm64": "1.0.60", - "@github/copilot-win32-x64": "1.0.60" + "@github/copilot-darwin-arm64": "1.0.61", + "@github/copilot-darwin-x64": "1.0.61", + "@github/copilot-linux-arm64": "1.0.61", + "@github/copilot-linux-x64": "1.0.61", + "@github/copilot-linuxmusl-arm64": "1.0.61", + "@github/copilot-linuxmusl-x64": "1.0.61", + "@github/copilot-win32-arm64": "1.0.61", + "@github/copilot-win32-x64": "1.0.61" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.60.tgz", - "integrity": "sha512-TErNaVxsv+uB3bdHwdoKorCd1rhiRh7HkX48vnS7jwqa8EtGgAkzNrHKC7mruL2rnYOOsNIdPfhzQk+2Y6PSxQ==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.61.tgz", + "integrity": "sha512-10prvjHRXB0SD28NsIpzdNDgLquQYUwaH5Ev9KVdIWdBPAvlQsHmQ4JSCyD/UILc/nrrr02CKUgum+mZRKUKIg==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.60.tgz", - "integrity": "sha512-PthhcR6PqbQlT04xQKTElpPSJOrJd65nK/l9Sjmpwtk21RrDKs13DCY/19ubP17updYUWBxp3VNfyfN3DAQKOA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.61.tgz", + "integrity": "sha512-NXUjageJ3mxDfHtXGYu//XhJ+dhJFYObT4R3jeWgIHhd+4lX7FlC754nwlBP/ZuVhJ3ND22JK9sua9d2F3Cbwg==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.60.tgz", - "integrity": "sha512-AVahkDVQTiGmHvDjlb4CHO8CFEGqmCEipxi0qTA60oH3Y3W2C4aYBwEBtP/85pN3wUUKZJVrWTCcxdufUBuK2Q==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.61.tgz", + "integrity": "sha512-dwB2+QSMr622JkePeK56M7YWXsTT/DQzKfpDq8Lk2kmGU052RZAarRmt8gcNm4anofN7pMSrqc3YHj1TM84MFw==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.60.tgz", - "integrity": "sha512-NwQjV2ZyUdJVAO4t7wiT+eR3uNWYP57xaLUIhf6JTMGpsTyN+mAFXW63xpwM/K+Pug62uRDQDBjEeOQRB7qZrA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.61.tgz", + "integrity": "sha512-q6n8R8oybvuCmmkP+43w809Wpud/wwRi/fFSZEYJagiNGmYJ00SDkrfJxHbZsAFMpaJC+oTswqzJHjRoZbO74w==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.60.tgz", - "integrity": "sha512-AYGPc9vq2k248bVwUbiVJ65kIYYMQQ7ci+S3oefWBIyYtYwAH0n+Q/IGAj49IPrelBarYABAsX+EQZJJC8rhxw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.61.tgz", + "integrity": "sha512-yWo7JXnZS11eJpm68E1RWKMR47EwzPKj3V7GX0EMTd8Fw0T2Aurk9wt9p3c9w0v02nTO1DqJhi68KVWJPdVqvA==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.60.tgz", - "integrity": "sha512-9/F7yl0/9FpGvYR/TCQtbhu0vIaUVem6U7em85QYaEjkS45nK500pByCMWY0bXv2eSS8U2g+8FOAjfkyLlxwPw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.61.tgz", + "integrity": "sha512-nHzx27Ac4B0fpD9CcmvyrGOBEMJ01CPRgVRP0yAl4wpU4cM2I6+9TPyfYThlWDqZqiUKGXC1ZRQ+B8cJREVGmA==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.60.tgz", - "integrity": "sha512-ZxxS+Ua1+7Puz80yTOpQ4WS+s32NjrxIsqo8gE0FpuZId16BGOGbWkzWQvR/k2AVBCqpLZ7SK3LfDVKuKJRbpA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.61.tgz", + "integrity": "sha512-k6knzI+K5HlZeJDS/yeJAfoYD4xcURWfuqunpTCyk1pDbIFxmrLSqR/TDi7KNlpsf883n5WqpnB06K5kysdHHQ==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.60.tgz", - "integrity": "sha512-e91ZlFz9J1lkadExLg36oN8Ms/xIa03vAEir3DmyCeYebZ+Y48vdS+BwhQEma+GLoxJUOhzHndCckGnMRfNIbA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.61.tgz", + "integrity": "sha512-L6NZ6o73VZFHd7OoRaztV3Prh1PbW9HXqYsAx+XywNALQvE1u489WBUC1ggfYBW5MTBCf8mxSkYQdb3Am2omsw==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 4040da3c7..a932d0a3b 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "json-schema": "^0.4.0", "tsx": "^4.20.6" } diff --git a/java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java b/java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java index c3c4c64e0..b4d96764f 100644 --- a/java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java @@ -35,7 +35,9 @@ public final class HookProgressEvent extends SessionEvent { @JsonInclude(JsonInclude.Include.NON_NULL) public record HookProgressEventData( /** Human-readable progress message from the hook process */ - @JsonProperty("message") String message + @JsonProperty("message") String message, + /** When true, this status message replaces the previous temporary one instead of accumulating */ + @JsonProperty("temporary") Boolean temporary ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java index e06451b3f..b1cfda933 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java @@ -39,6 +39,8 @@ public record SessionResumeEventData( @JsonProperty("resumeTime") OffsetDateTime resumeTime, /** Total number of persisted events in the session at the time of resume */ @JsonProperty("eventCount") Long eventCount, + /** On-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd */ + @JsonProperty("eventsFileSizeBytes") Long eventsFileSizeBytes, /** Model currently selected at resume time */ @JsonProperty("selectedModel") String selectedModel, /** Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") */ diff --git a/java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java index 653622e5f..a7a934949 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java @@ -36,8 +36,14 @@ public final class SessionScheduleCreatedEvent extends SessionEvent { public record SessionScheduleCreatedEventData( /** Sequential id assigned to the scheduled prompt within the session */ @JsonProperty("id") Long id, - /** Interval between ticks in milliseconds */ + /** Interval between ticks in milliseconds (relative-interval schedules) */ @JsonProperty("intervalMs") Long intervalMs, + /** 5-field cron expression for a recurring calendar schedule, evaluated in `tz` */ + @JsonProperty("cron") String cron, + /** IANA timezone the `cron` expression is evaluated in */ + @JsonProperty("tz") String tz, + /** Absolute fire time (epoch milliseconds) for a one-shot calendar schedule */ + @JsonProperty("at") Long at, /** Prompt text that gets enqueued on every tick */ @JsonProperty("prompt") String prompt, /** Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) */ diff --git a/java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java index e1609588c..84c302306 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java @@ -49,6 +49,8 @@ public record SessionShutdownEventData( @JsonProperty("totalApiDurationMs") Long totalApiDurationMs, /** Unix timestamp (milliseconds) when the session started */ @JsonProperty("sessionStartTime") Long sessionStartTime, + /** On-disk byte size of the session's persisted events.jsonl file at shutdown time; omitted when the file does not exist or cannot be stat'd */ + @JsonProperty("eventsFileSizeBytes") Long eventsFileSizeBytes, /** Aggregate code change metrics for the session */ @JsonProperty("codeChanges") ShutdownCodeChanges codeChanges, /** Per-model usage breakdown, keyed by model identifier */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java new file mode 100644 index 000000000..39cee86de --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include in agent discovery. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AgentsDiscoverParams( + /** Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). */ + @JsonProperty("projectPaths") List projectPaths, + /** When true, omit the host's agents (the `/agents` directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. */ + @JsonProperty("excludeHostAgents") Boolean excludeHostAgents +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java new file mode 100644 index 000000000..50791127e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Agents discovered across user, project, plugin, and remote sources. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AgentsDiscoverResult( + /** All discovered agents across all sources */ + @JsonProperty("agents") List agents +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSources.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java similarity index 73% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSources.java rename to java/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java index 9e6a458d4..37d1ead1c 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSources.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java @@ -14,14 +14,14 @@ import javax.annotation.processing.Generated; /** - * Schema for the `InstructionsSources` type. + * Schema for the `InstructionSource` type. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record InstructionsSources( +public record InstructionSource( /** Unique identifier for this source (used for toggling) */ @JsonProperty("id") String id, /** Human-readable label */ @@ -31,14 +31,16 @@ public record InstructionsSources( /** Raw content of the instruction file */ @JsonProperty("content") String content, /** Category of instruction source — used for merge logic */ - @JsonProperty("type") InstructionsSourcesType type, + @JsonProperty("type") InstructionSourceType type, /** Where this source lives — used for UI grouping */ - @JsonProperty("location") InstructionsSourcesLocation location, + @JsonProperty("location") InstructionSourceLocation location, /** Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files */ @JsonProperty("applyTo") List applyTo, /** Short description (body after frontmatter) for use in instruction tables */ @JsonProperty("description") String description, /** When true, this source starts disabled and must be toggled on by the user */ - @JsonProperty("defaultDisabled") Boolean defaultDisabled + @JsonProperty("defaultDisabled") Boolean defaultDisabled, + /** The project path this source was discovered from. Only set by sessionless discovery for repository/working-directory sources, where it disambiguates same-named files (e.g. .github/copilot-instructions.md) across multiple workspace roots. The session-scoped getSources leaves it unset. */ + @JsonProperty("projectPath") String projectPath ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesLocation.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java similarity index 76% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesLocation.java rename to java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java index 23db5a367..261327cfb 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesLocation.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java @@ -15,7 +15,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum InstructionsSourcesLocation { +public enum InstructionSourceLocation { /** The {@code user} variant. */ USER("user"), /** The {@code repository} variant. */ @@ -26,14 +26,14 @@ public enum InstructionsSourcesLocation { PLUGIN("plugin"); private final String value; - InstructionsSourcesLocation(String value) { this.value = value; } + InstructionSourceLocation(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator - public static InstructionsSourcesLocation fromValue(String value) { - for (InstructionsSourcesLocation v : values()) { + public static InstructionSourceLocation fromValue(String value) { + for (InstructionSourceLocation v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown InstructionsSourcesLocation value: " + value); + throw new IllegalArgumentException("Unknown InstructionSourceLocation value: " + value); } } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesType.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java similarity index 80% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesType.java rename to java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java index 6fed6c4bf..d267e249f 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsSourcesType.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java @@ -15,7 +15,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum InstructionsSourcesType { +public enum InstructionSourceType { /** The {@code home} variant. */ HOME("home"), /** The {@code repo} variant. */ @@ -32,14 +32,14 @@ public enum InstructionsSourcesType { PLUGIN("plugin"); private final String value; - InstructionsSourcesType(String value) { this.value = value; } + InstructionSourceType(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator - public static InstructionsSourcesType fromValue(String value) { - for (InstructionsSourcesType v : values()) { + public static InstructionSourceType fromValue(String value) { + for (InstructionSourceType v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown InstructionsSourcesType value: " + value); + throw new IllegalArgumentException("Unknown InstructionSourceType value: " + value); } } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java new file mode 100644 index 000000000..1a0b84051 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Optional project paths to include in instruction discovery. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record InstructionsDiscoverParams( + /** Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). */ + @JsonProperty("projectPaths") List projectPaths, + /** When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. */ + @JsonProperty("excludeHostInstructions") Boolean excludeHostInstructions +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java new file mode 100644 index 000000000..e8cdee9e0 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Instruction sources discovered across user, repository, and plugin sources. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record InstructionsDiscoverResult( + /** All discovered instruction sources */ + @JsonProperty("sources") List sources +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java b/java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java index fb41975bd..f8d58ad77 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java @@ -24,8 +24,14 @@ public record ScheduleEntry( /** Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). */ @JsonProperty("id") Long id, - /** Interval between scheduled ticks, in milliseconds. */ + /** Interval between scheduled ticks, in milliseconds (relative-interval schedules). */ @JsonProperty("intervalMs") Long intervalMs, + /** 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. */ + @JsonProperty("cron") String cron, + /** IANA timezone the `cron` expression is evaluated in. */ + @JsonProperty("tz") String tz, + /** Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. */ + @JsonProperty("at") Long at, /** Prompt text that gets enqueued on every tick. */ @JsonProperty("prompt") String prompt, /** Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java new file mode 100644 index 000000000..6a73f4cbc --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code agents} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerAgentsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerAgentsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Optional project paths to include in agent discovery. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover(AgentsDiscoverParams params) { + return caller.invoke("agents.discover", params, AgentsDiscoverResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java new file mode 100644 index 000000000..af5ac2719 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code instructions} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerInstructionsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerInstructionsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Optional project paths to include in instruction discovery. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover(InstructionsDiscoverParams params) { + return caller.invoke("instructions.discover", params, InstructionsDiscoverResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index 435de1054..f96c87386 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -38,6 +38,10 @@ public final class ServerRpc { public final ServerPluginsApi plugins; /** API methods for the {@code skills} namespace. */ public final ServerSkillsApi skills; + /** API methods for the {@code agents} namespace. */ + public final ServerAgentsApi agents; + /** API methods for the {@code instructions} namespace. */ + public final ServerInstructionsApi instructions; /** API methods for the {@code user} namespace. */ public final ServerUserApi user; /** API methods for the {@code runtime} namespace. */ @@ -63,6 +67,8 @@ public ServerRpc(RpcCaller caller) { this.mcp = new ServerMcpApi(caller); this.plugins = new ServerPluginsApi(caller); this.skills = new ServerSkillsApi(caller); + this.agents = new ServerAgentsApi(caller); + this.instructions = new ServerInstructionsApi(caller); this.user = new ServerUserApi(caller); this.runtime = new ServerRuntimeApi(caller); this.sessionFs = new ServerSessionFsApi(caller); diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java index f5275aa54..b798c1d65 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java @@ -26,6 +26,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionInstructionsGetSourcesResult( /** Instruction sources for the session */ - @JsonProperty("sources") List sources + @JsonProperty("sources") List sources ) { } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index fa7e47207..2c9823233 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -697,9 +697,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.60.tgz", - "integrity": "sha512-+GjW+GJNo55nwJwt48o9szWcyhuY0u682cBKQI1ay9jVBX8DCCXC6HB6Tyv5/MaM4N7CxTiEgp48aVMkye8K+g==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.61.tgz", + "integrity": "sha512-E4f7YXTL2uUZY/ypnfsUruAeSgrHx3AGYEbm5N0DrpzPqoNAZqV6kHEWM4vu+W/nGvydIfPxmOTqaMEhM8r0Uw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -708,20 +708,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.60", - "@github/copilot-darwin-x64": "1.0.60", - "@github/copilot-linux-arm64": "1.0.60", - "@github/copilot-linux-x64": "1.0.60", - "@github/copilot-linuxmusl-arm64": "1.0.60", - "@github/copilot-linuxmusl-x64": "1.0.60", - "@github/copilot-win32-arm64": "1.0.60", - "@github/copilot-win32-x64": "1.0.60" + "@github/copilot-darwin-arm64": "1.0.61", + "@github/copilot-darwin-x64": "1.0.61", + "@github/copilot-linux-arm64": "1.0.61", + "@github/copilot-linux-x64": "1.0.61", + "@github/copilot-linuxmusl-arm64": "1.0.61", + "@github/copilot-linuxmusl-x64": "1.0.61", + "@github/copilot-win32-arm64": "1.0.61", + "@github/copilot-win32-x64": "1.0.61" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.60.tgz", - "integrity": "sha512-TErNaVxsv+uB3bdHwdoKorCd1rhiRh7HkX48vnS7jwqa8EtGgAkzNrHKC7mruL2rnYOOsNIdPfhzQk+2Y6PSxQ==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.61.tgz", + "integrity": "sha512-10prvjHRXB0SD28NsIpzdNDgLquQYUwaH5Ev9KVdIWdBPAvlQsHmQ4JSCyD/UILc/nrrr02CKUgum+mZRKUKIg==", "cpu": [ "arm64" ], @@ -735,9 +735,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.60.tgz", - "integrity": "sha512-PthhcR6PqbQlT04xQKTElpPSJOrJd65nK/l9Sjmpwtk21RrDKs13DCY/19ubP17updYUWBxp3VNfyfN3DAQKOA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.61.tgz", + "integrity": "sha512-NXUjageJ3mxDfHtXGYu//XhJ+dhJFYObT4R3jeWgIHhd+4lX7FlC754nwlBP/ZuVhJ3ND22JK9sua9d2F3Cbwg==", "cpu": [ "x64" ], @@ -751,9 +751,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.60.tgz", - "integrity": "sha512-AVahkDVQTiGmHvDjlb4CHO8CFEGqmCEipxi0qTA60oH3Y3W2C4aYBwEBtP/85pN3wUUKZJVrWTCcxdufUBuK2Q==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.61.tgz", + "integrity": "sha512-dwB2+QSMr622JkePeK56M7YWXsTT/DQzKfpDq8Lk2kmGU052RZAarRmt8gcNm4anofN7pMSrqc3YHj1TM84MFw==", "cpu": [ "arm64" ], @@ -767,9 +767,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.60.tgz", - "integrity": "sha512-NwQjV2ZyUdJVAO4t7wiT+eR3uNWYP57xaLUIhf6JTMGpsTyN+mAFXW63xpwM/K+Pug62uRDQDBjEeOQRB7qZrA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.61.tgz", + "integrity": "sha512-q6n8R8oybvuCmmkP+43w809Wpud/wwRi/fFSZEYJagiNGmYJ00SDkrfJxHbZsAFMpaJC+oTswqzJHjRoZbO74w==", "cpu": [ "x64" ], @@ -783,9 +783,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.60.tgz", - "integrity": "sha512-AYGPc9vq2k248bVwUbiVJ65kIYYMQQ7ci+S3oefWBIyYtYwAH0n+Q/IGAj49IPrelBarYABAsX+EQZJJC8rhxw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.61.tgz", + "integrity": "sha512-yWo7JXnZS11eJpm68E1RWKMR47EwzPKj3V7GX0EMTd8Fw0T2Aurk9wt9p3c9w0v02nTO1DqJhi68KVWJPdVqvA==", "cpu": [ "arm64" ], @@ -799,9 +799,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.60.tgz", - "integrity": "sha512-9/F7yl0/9FpGvYR/TCQtbhu0vIaUVem6U7em85QYaEjkS45nK500pByCMWY0bXv2eSS8U2g+8FOAjfkyLlxwPw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.61.tgz", + "integrity": "sha512-nHzx27Ac4B0fpD9CcmvyrGOBEMJ01CPRgVRP0yAl4wpU4cM2I6+9TPyfYThlWDqZqiUKGXC1ZRQ+B8cJREVGmA==", "cpu": [ "x64" ], @@ -815,9 +815,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.60.tgz", - "integrity": "sha512-ZxxS+Ua1+7Puz80yTOpQ4WS+s32NjrxIsqo8gE0FpuZId16BGOGbWkzWQvR/k2AVBCqpLZ7SK3LfDVKuKJRbpA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.61.tgz", + "integrity": "sha512-k6knzI+K5HlZeJDS/yeJAfoYD4xcURWfuqunpTCyk1pDbIFxmrLSqR/TDi7KNlpsf883n5WqpnB06K5kysdHHQ==", "cpu": [ "arm64" ], @@ -831,9 +831,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.60.tgz", - "integrity": "sha512-e91ZlFz9J1lkadExLg36oN8Ms/xIa03vAEir3DmyCeYebZ+Y48vdS+BwhQEma+GLoxJUOhzHndCckGnMRfNIbA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.61.tgz", + "integrity": "sha512-L6NZ6o73VZFHd7OoRaztV3Prh1PbW9HXqYsAx+XywNALQvE1u489WBUC1ggfYBW5MTBCf8mxSkYQdb3Am2omsw==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 95e626d35..df34b9df7 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index e82d971ad..d80779f81 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 53aa71fd2..c17a21d21 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -423,10 +423,10 @@ export type InstalledPluginSource = * Category of instruction source — used for merge logic * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "InstructionsSourcesType". + * via the `definition` "InstructionSourceType". */ /** @experimental */ -export type InstructionsSourcesType = +export type InstructionSourceType = /** Instructions loaded from the user's home configuration. */ | "home" /** Instructions loaded from repository-scoped files. */ @@ -445,10 +445,10 @@ export type InstructionsSourcesType = * Where this source lives — used for UI grouping * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "InstructionsSourcesLocation". + * via the `definition` "InstructionSourceLocation". */ /** @experimental */ -export type InstructionsSourcesLocation = +export type InstructionSourceLocation = /** Instructions live in user-level configuration. */ | "user" /** Instructions live in repository-level configuration. */ @@ -2011,6 +2011,23 @@ export interface AgentReloadResult { */ agents: AgentInfo[]; } +/** + * Optional project paths to include in agent discovery. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AgentsDiscoverRequest". + */ +/** @experimental */ +export interface AgentsDiscoverRequest { + /** + * Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + */ + projectPaths?: string[]; + /** + * When true, omit the host's agents (the `/agents` directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + */ + excludeHostAgents?: boolean; +} /** * Name of the custom agent to select for subsequent turns. * @@ -3964,6 +3981,23 @@ export interface InstalledPluginInfo { */ enabled: boolean; } +/** + * Optional project paths to include in instruction discovery. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "InstructionsDiscoverRequest". + */ +/** @experimental */ +export interface InstructionsDiscoverRequest { + /** + * Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + */ + projectPaths?: string[]; + /** + * When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + */ + excludeHostInstructions?: boolean; +} /** * Instruction sources loaded for the session, in merge order. * @@ -3975,16 +4009,16 @@ export interface InstructionsGetSourcesResult { /** * Instruction sources for the session */ - sources: InstructionsSources[]; + sources: InstructionSource[]; } /** - * Schema for the `InstructionsSources` type. + * Schema for the `InstructionSource` type. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "InstructionsSources". + * via the `definition` "InstructionSource". */ /** @experimental */ -export interface InstructionsSources { +export interface InstructionSource { /** * Unique identifier for this source (used for toggling) */ @@ -4001,8 +4035,8 @@ export interface InstructionsSources { * Raw content of the instruction file */ content: string; - type: InstructionsSourcesType; - location: InstructionsSourcesLocation; + type: InstructionSourceType; + location: InstructionSourceLocation; /** * Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files */ @@ -4015,6 +4049,10 @@ export interface InstructionsSources { * When true, this source starts disabled and must be toggled on by the user */ defaultDisabled?: boolean; + /** + * The project path this source was discovered from. Only set by sessionless discovery for repository/working-directory sources, where it disambiguates same-named files (e.g. .github/copilot-instructions.md) across multiple workspace roots. The session-scoped getSources leaves it unset. + */ + projectPath?: string; } /** * Schema for the `LocalSessionMetadataValue` type. @@ -8553,9 +8591,21 @@ export interface ScheduleEntry { */ id: number; /** - * Interval between scheduled ticks, in milliseconds. + * Interval between scheduled ticks, in milliseconds (relative-interval schedules). + */ + intervalMs?: number; + /** + * 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + */ + cron?: string; + /** + * IANA timezone the `cron` expression is evaluated in. + */ + tz?: string; + /** + * Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. */ - intervalMs: number; + at?: number; /** * Prompt text that gets enqueued on every tick. */ @@ -8722,6 +8772,32 @@ export interface SendResult { */ messageId: string; } +/** + * Agents discovered across user, project, plugin, and remote sources. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ServerAgentList". + */ +/** @experimental */ +export interface ServerAgentList { + /** + * All discovered agents across all sources + */ + agents: AgentInfo[]; +} +/** + * Instruction sources discovered across user, repository, and plugin sources. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ServerInstructionSourceList". + */ +/** @experimental */ +export interface ServerInstructionSourceList { + /** + * All discovered instruction sources + */ + sources: InstructionSource[]; +} /** * Schema for the `ServerSkill` type. * @@ -11112,9 +11188,13 @@ export interface TaskAgentInfo { */ result?: string; /** - * Model used for the task when specified + * Requested model override for the task when specified */ model?: string; + /** + * Runtime model resolved for the task when available + */ + resolvedModel?: string; executionMode?: TaskExecutionMode; /** * Whether the task is currently in the original sync wait and can be moved to background mode. False once it is already backgrounded, idle, finished, or no longer has a promotable sync waiter. @@ -12838,6 +12918,30 @@ export function createServerRpc(connection: MessageConnection) { discover: async (params: SkillsDiscoverRequest): Promise => connection.sendRequest("skills.discover", params), }, + /** @experimental */ + agents: { + /** + * Discovers custom agents across user, project, plugin, and remote sources. + * + * @param params Optional project paths to include in agent discovery. + * + * @returns Agents discovered across user, project, plugin, and remote sources. + */ + discover: async (params: AgentsDiscoverRequest): Promise => + connection.sendRequest("agents.discover", params), + }, + /** @experimental */ + instructions: { + /** + * Discovers instruction sources across user, repository, and plugin sources. + * + * @param params Optional project paths to include in instruction discovery. + * + * @returns Instruction sources discovered across user, repository, and plugin sources. + */ + discover: async (params: InstructionsDiscoverRequest): Promise => + connection.sendRequest("instructions.discover", params), + }, user: { settings: { /** @@ -13673,15 +13777,6 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ reload: async (): Promise => connection.sendRequest("session.mcp.reload", { sessionId }), - /** - * Reloads MCP server connections for the session with an explicit host-provided configuration. - * - * @param params Opaque MCP reload configuration. - * - * @returns MCP server startup filtering result. - */ - reloadWithConfig: async (params: McpReloadWithConfigRequest): Promise => - connection.sendRequest("session.mcp.reloadWithConfig", { sessionId, ...params }), /** * Runs an MCP sampling inference on behalf of an MCP server. * @@ -13716,29 +13811,6 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ removeGitHub: async (): Promise => connection.sendRequest("session.mcp.removeGitHub", { sessionId }), - /** - * Configures the built-in GitHub MCP server for the session's current auth context. - * - * @param params Opaque auth info used to configure GitHub MCP. - * - * @returns Result of configuring GitHub MCP. - */ - configureGitHub: async (params: McpConfigureGitHubRequest): Promise => - connection.sendRequest("session.mcp.configureGitHub", { sessionId, ...params }), - /** - * Starts an individual MCP server on the session's host. - * - * @param params Server name and opaque configuration for an individual MCP server start. - */ - startServer: async (params: McpStartServerRequest): Promise => - connection.sendRequest("session.mcp.startServer", { sessionId, ...params }), - /** - * Restarts an individual MCP server on the session's host (stops then starts). - * - * @param params Server name and opaque configuration for an individual MCP server restart. - */ - restartServer: async (params: McpRestartServerRequest): Promise => - connection.sendRequest("session.mcp.restartServer", { sessionId, ...params }), /** * Stops an individual MCP server on the session's host. * @@ -13746,20 +13818,6 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ stopServer: async (params: McpStopServerRequest): Promise => connection.sendRequest("session.mcp.stopServer", { sessionId, ...params }), - /** - * Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself. - * - * @param params Registration parameters for an external MCP client. - */ - registerExternalClient: async (params: McpRegisterExternalClientRequest): Promise => - connection.sendRequest("session.mcp.registerExternalClient", { sessionId, ...params }), - /** - * Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime. - * - * @param params Server name identifying the external client to remove. - */ - unregisterExternalClient: async (params: McpUnregisterExternalClientRequest): Promise => - connection.sendRequest("session.mcp.unregisterExternalClient", { sessionId, ...params }), /** * Checks whether a named MCP server is currently running on the session's host. * @@ -13771,15 +13829,6 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin connection.sendRequest("session.mcp.isServerRunning", { sessionId, ...params }), /** @experimental */ oauth: { - /** - * Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK. - * - * @param params MCP OAuth request id and optional provider response. - * - * @returns Empty result after recording the MCP OAuth response. - */ - respond: async (params: McpOauthRespondRequest): Promise => - connection.sendRequest("session.mcp.oauth.respond", { sessionId, ...params }), /** * Starts OAuth authentication for a remote MCP server. * @@ -14566,6 +14615,77 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin }; } +/** + * Create typed session-scoped RPC methods that are part of the SDK's internal + * surface. Not exported on the public client API. + * @internal + */ +export function createInternalSessionRpc(connection: MessageConnection, sessionId: string) { + return { + /** @experimental */ + mcp: { + /** + * Reloads MCP server connections for the session with an explicit host-provided configuration. + * + * @param params Opaque MCP reload configuration. + * + * @returns MCP server startup filtering result. + */ + reloadWithConfig: async (params: McpReloadWithConfigRequest): Promise => + connection.sendRequest("session.mcp.reloadWithConfig", { sessionId, ...params }), + /** + * Configures the built-in GitHub MCP server for the session's current auth context. + * + * @param params Opaque auth info used to configure GitHub MCP. + * + * @returns Result of configuring GitHub MCP. + */ + configureGitHub: async (params: McpConfigureGitHubRequest): Promise => + connection.sendRequest("session.mcp.configureGitHub", { sessionId, ...params }), + /** + * Starts an individual MCP server on the session's host. + * + * @param params Server name and opaque configuration for an individual MCP server start. + */ + startServer: async (params: McpStartServerRequest): Promise => + connection.sendRequest("session.mcp.startServer", { sessionId, ...params }), + /** + * Restarts an individual MCP server on the session's host (stops then starts). + * + * @param params Server name and opaque configuration for an individual MCP server restart. + */ + restartServer: async (params: McpRestartServerRequest): Promise => + connection.sendRequest("session.mcp.restartServer", { sessionId, ...params }), + /** + * Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself. + * + * @param params Registration parameters for an external MCP client. + */ + registerExternalClient: async (params: McpRegisterExternalClientRequest): Promise => + connection.sendRequest("session.mcp.registerExternalClient", { sessionId, ...params }), + /** + * Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime. + * + * @param params Server name identifying the external client to remove. + */ + unregisterExternalClient: async (params: McpUnregisterExternalClientRequest): Promise => + connection.sendRequest("session.mcp.unregisterExternalClient", { sessionId, ...params }), + /** @experimental */ + oauth: { + /** + * Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK. + * + * @param params MCP OAuth request id and optional provider response. + * + * @returns Empty result after recording the MCP OAuth response. + */ + respond: async (params: McpOauthRespondRequest): Promise => + connection.sendRequest("session.mcp.oauth.respond", { sessionId, ...params }), + }, + }, + }; +} + /** Handler for `sessionFs` client session API methods. */ /** @experimental */ export interface SessionFsHandler { diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 77a354cb6..a4fba8f33 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -715,6 +715,10 @@ export interface ResumeData { * Total number of persisted events in the session at the time of resume */ eventCount: number; + /** + * On-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd + */ + eventsFileSizeBytes?: number; /** * Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") */ @@ -959,6 +963,14 @@ export interface ScheduleCreatedEvent { * Scheduled prompt registered via /every or /after */ export interface ScheduleCreatedData { + /** + * Absolute fire time (epoch milliseconds) for a one-shot calendar schedule + */ + at?: number; + /** + * 5-field cron expression for a recurring calendar schedule, evaluated in `tz` + */ + cron?: string; /** * Optional user-facing label shown in the timeline instead of the actual prompt (e.g. `/skill-name args` when the prompt is a skill invocation expansion) */ @@ -968,9 +980,9 @@ export interface ScheduleCreatedData { */ id: number; /** - * Interval between ticks in milliseconds + * Interval between ticks in milliseconds (relative-interval schedules) */ - intervalMs: number; + intervalMs?: number; /** * Prompt text that gets enqueued on every tick */ @@ -979,6 +991,10 @@ export interface ScheduleCreatedData { * Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) */ recurring?: boolean; + /** + * IANA timezone the `cron` expression is evaluated in + */ + tz?: string; } /** * Session event "session.schedule_cancelled". Scheduled prompt cancelled from the schedule manager dialog @@ -1610,6 +1626,10 @@ export interface ShutdownData { * Error description when shutdownType is "error" */ errorReason?: string; + /** + * On-disk byte size of the session's persisted events.jsonl file at shutdown time; omitted when the file does not exist or cannot be stat'd + */ + eventsFileSizeBytes?: number; /** * Per-model usage breakdown, keyed by model identifier */ @@ -4271,6 +4291,10 @@ export interface HookProgressData { * Human-readable progress message from the hook process */ message: string; + /** + * When true, this status message replaces the previous temporary one instead of accumulating + */ + temporary?: boolean; } /** * Session event "system.message". System/developer instruction content with role and optional template metadata diff --git a/nodejs/test/e2e/abort.e2e.test.ts b/nodejs/test/e2e/abort.e2e.test.ts index 87d91fc5e..89877387c 100644 --- a/nodejs/test/e2e/abort.e2e.test.ts +++ b/nodejs/test/e2e/abort.e2e.test.ts @@ -58,11 +58,32 @@ describe("Abort", async () => { // Abort mid-stream await session.abort(); - // Session should be usable after abort — send a follow-up and get a response - const followUp = await session.sendAndWait({ - prompt: "Say 'abort_recovery_ok'.", + // Session should be usable after abort. Wait for the specific recovery + // message rather than racing against a late idle from the aborted turn. + let recoveryResolve!: (content: string) => void; + const recoveryReceived = new Promise((resolve) => { + recoveryResolve = resolve; + }); + const unsubscribeRecovery = session.on((event) => { + if (event.type === "assistant.message") { + const content = event.data.content ?? ""; + if (content.toLowerCase().includes("abort_recovery_ok")) { + recoveryResolve(content); + } + } }); - expect(followUp?.data.content?.toLowerCase()).toContain("abort_recovery_ok"); + + try { + await session.send({ prompt: "Say 'abort_recovery_ok'." }); + const recoveryContent = await withTimeout( + recoveryReceived, + 60_000, + "assistant.message containing abort_recovery_ok" + ); + expect(recoveryContent.toLowerCase()).toContain("abort_recovery_ok"); + } finally { + unsubscribeRecovery(); + } await session.disconnect(); }); diff --git a/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts b/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts index 22db56d9b..40f837434 100644 --- a/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts +++ b/nodejs/test/e2e/rpc_mcp_lifecycle.e2e.test.ts @@ -148,115 +148,4 @@ describe("Session-scoped MCP lifecycle RPC", async () => { await session.disconnect(); } }); - - it("should start and restart mcp server", { timeout: 180_000 }, async () => { - const hostServer = "rpc-lifecycle-host-server"; - const session = await createSessionWithMcp(hostServer); - try { - await waitForMcpServerStatus(session, hostServer); - - const startedServer = "rpc-lifecycle-started-server"; - const config = createTestMcpServers(startedServer)[startedServer] as unknown as Record< - string, - unknown - >; - - await session.rpc.mcp.startServer({ serverName: startedServer, config }); - await waitForMcpRunning(session, startedServer, true); - - const tools = await session.rpc.mcp.listTools({ serverName: startedServer }); - expect(tools.tools.length).toBeGreaterThan(0); - - await session.rpc.mcp.restartServer({ serverName: startedServer, config }); - await waitForMcpRunning(session, startedServer, true); - } finally { - await session.disconnect(); - } - }); - - it("should register and unregister external mcp client", { timeout: 120_000 }, async () => { - const hostServer = "rpc-lifecycle-extclient-host"; - const session = await createSessionWithMcp(hostServer); - try { - await waitForMcpServerStatus(session, hostServer); - - const externalName = "rpc-lifecycle-external-client"; - expect( - (await session.rpc.mcp.isServerRunning({ serverName: externalName })).running - ).toBe(false); - - await session.rpc.mcp.registerExternalClient({ - serverName: externalName, - client: { id: externalName }, - transport: { kind: "in-process" }, - config: { command: "noop" }, - }); - expect( - (await session.rpc.mcp.isServerRunning({ serverName: externalName })).running - ).toBe(true); - - await session.rpc.mcp.unregisterExternalClient({ serverName: externalName }); - expect( - (await session.rpc.mcp.isServerRunning({ serverName: externalName })).running - ).toBe(false); - } finally { - await session.disconnect(); - } - }); - - it("should reload mcp servers with config", { timeout: 120_000 }, async () => { - const hostServer = "rpc-lifecycle-reload-host"; - const session = await createSessionWithMcp(hostServer); - try { - await waitForMcpServerStatus(session, hostServer); - - const result = await session.rpc.mcp.reloadWithConfig({ - config: { - mcpServers: {}, - disabledServers: [], - }, - }); - - expect(result).toBeDefined(); - expect(result.filteredServers).toEqual([]); - } finally { - await session.disconnect(); - } - }); - - it("should configure github mcp server", { timeout: 120_000 }, async () => { - const hostServer = "rpc-lifecycle-configure-host"; - const session = await createSessionWithMcp(hostServer); - try { - await waitForMcpServerStatus(session, hostServer); - - const result = await session.rpc.mcp.configureGitHub({ - authInfo: { type: "api-key" }, - }); - - expect(result).toBeDefined(); - expect(result.changed).toBe(false); - } finally { - await session.disconnect(); - } - }); - - it( - "should respond to mcp oauth request without pending request", - { timeout: 120_000 }, - async () => { - const hostServer = "rpc-lifecycle-oauth-host"; - const session = await createSessionWithMcp(hostServer); - try { - await waitForMcpServerStatus(session, hostServer); - - const result = await session.rpc.mcp.oauth.respond({ - requestId: missingName("missing"), - }); - expect(result).toBeDefined(); - } finally { - await session.disconnect(); - } - } - ); }); diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 0f2a57069..69bf16007 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -314,6 +314,35 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentsDiscoverRequest: + """Optional project paths to include in agent discovery.""" + + exclude_host_agents: bool | None = None + """When true, omit the host's agents (the `/agents` directory and all plugin + agents), leaving only project and remote agents. For multitenant deployments. + """ + project_paths: list[str] | None = None + """Optional list of project directory paths to scan for project-scoped agents. When omitted + or empty, only user/plugin/remote-independent agents are returned (no project scan). + """ + + @staticmethod + def from_dict(obj: Any) -> 'AgentsDiscoverRequest': + assert isinstance(obj, dict) + exclude_host_agents = from_union([from_bool, from_none], obj.get("excludeHostAgents")) + project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) + return AgentsDiscoverRequest(exclude_host_agents, project_paths) + + def to_dict(self) -> dict: + result: dict = {} + if self.exclude_host_agents is not None: + result["excludeHostAgents"] = from_union([from_bool, from_none], self.exclude_host_agents) + if self.project_paths is not None: + result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AllowAllPermissionSetResult: @@ -1706,7 +1735,7 @@ class StickySource(Enum): URL = "url" # Experimental: this type is part of an experimental API and may change or be removed. -class InstructionsSourcesLocation(Enum): +class InstructionSourceLocation(Enum): """Where this source lives — used for UI grouping""" PLUGIN = "plugin" @@ -1715,7 +1744,7 @@ class InstructionsSourcesLocation(Enum): WORKING_DIRECTORY = "working-directory" # Experimental: this type is part of an experimental API and may change or be removed. -class InstructionsSourcesType(Enum): +class InstructionSourceType(Enum): """Category of instruction source — used for merge logic""" CHILD_INSTRUCTIONS = "child-instructions" @@ -1726,6 +1755,36 @@ class InstructionsSourcesType(Enum): REPO = "repo" VSCODE = "vscode" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class InstructionsDiscoverRequest: + """Optional project paths to include in instruction discovery.""" + + exclude_host_instructions: bool | None = None + """When true, omit the host's instruction sources (user/home-level files and plugin rules), + leaving only repository and working-directory sources. For multitenant deployments. + """ + project_paths: list[str] | None = None + """Optional list of project directory paths to scan for repository/working-directory + instruction sources. When omitted or empty, only user-level and plugin instruction + sources are returned (no project scan). + """ + + @staticmethod + def from_dict(obj: Any) -> 'InstructionsDiscoverRequest': + assert isinstance(obj, dict) + exclude_host_instructions = from_union([from_bool, from_none], obj.get("excludeHostInstructions")) + project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) + return InstructionsDiscoverRequest(exclude_host_instructions, project_paths) + + def to_dict(self) -> dict: + result: dict = {} + if self.exclude_host_instructions is not None: + result["excludeHostInstructions"] = from_union([from_bool, from_none], self.exclude_host_instructions) + if self.project_paths is not None: + result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class HostType(Enum): """Repository host type @@ -5172,9 +5231,6 @@ class ScheduleEntry: """Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). """ - interval_ms: int - """Interval between scheduled ticks, in milliseconds.""" - next_run_at: datetime """ISO 8601 timestamp when the next tick is scheduled to fire.""" @@ -5184,31 +5240,52 @@ class ScheduleEntry: recurring: bool """Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`).""" + at: int | None = None + """Absolute fire time (epoch milliseconds) for a one-shot calendar schedule.""" + + cron: str | None = None + """5-field cron expression for a recurring calendar schedule, evaluated in `tz`.""" + display_prompt: str | None = None """Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`. """ + interval_ms: int | None = None + """Interval between scheduled ticks, in milliseconds (relative-interval schedules).""" + + tz: str | None = None + """IANA timezone the `cron` expression is evaluated in.""" @staticmethod def from_dict(obj: Any) -> 'ScheduleEntry': assert isinstance(obj, dict) id = from_int(obj.get("id")) - interval_ms = from_int(obj.get("intervalMs")) next_run_at = from_datetime(obj.get("nextRunAt")) prompt = from_str(obj.get("prompt")) recurring = from_bool(obj.get("recurring")) + at = from_union([from_int, from_none], obj.get("at")) + cron = from_union([from_str, from_none], obj.get("cron")) display_prompt = from_union([from_str, from_none], obj.get("displayPrompt")) - return ScheduleEntry(id, interval_ms, next_run_at, prompt, recurring, display_prompt) + interval_ms = from_union([from_int, from_none], obj.get("intervalMs")) + tz = from_union([from_str, from_none], obj.get("tz")) + return ScheduleEntry(id, next_run_at, prompt, recurring, at, cron, display_prompt, interval_ms, tz) def to_dict(self) -> dict: result: dict = {} result["id"] = from_int(self.id) - result["intervalMs"] = from_int(self.interval_ms) result["nextRunAt"] = self.next_run_at.isoformat() result["prompt"] = from_str(self.prompt) result["recurring"] = from_bool(self.recurring) + if self.at is not None: + result["at"] = from_union([from_int, from_none], self.at) + if self.cron is not None: + result["cron"] = from_union([from_str, from_none], self.cron) if self.display_prompt is not None: result["displayPrompt"] = from_union([from_str, from_none], self.display_prompt) + if self.interval_ms is not None: + result["intervalMs"] = from_union([from_int, from_none], self.interval_ms) + if self.tz is not None: + result["tz"] = from_union([from_str, from_none], self.tz) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -9585,8 +9662,8 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class InstructionsSources: - """Schema for the `InstructionsSources` type.""" +class InstructionSource: + """Schema for the `InstructionSource` type.""" content: str """Raw content of the instruction file""" @@ -9597,13 +9674,13 @@ class InstructionsSources: label: str """Human-readable label""" - location: InstructionsSourcesLocation + location: InstructionSourceLocation """Where this source lives — used for UI grouping""" source_path: str """File path relative to repo or absolute for home""" - type: InstructionsSourcesType + type: InstructionSourceType """Category of instruction source — used for merge logic""" apply_to: list[str] | None = None @@ -9616,34 +9693,44 @@ class InstructionsSources: description: str | None = None """Short description (body after frontmatter) for use in instruction tables""" + project_path: str | None = None + """The project path this source was discovered from. Only set by sessionless discovery for + repository/working-directory sources, where it disambiguates same-named files (e.g. + .github/copilot-instructions.md) across multiple workspace roots. The session-scoped + getSources leaves it unset. + """ + @staticmethod - def from_dict(obj: Any) -> 'InstructionsSources': + def from_dict(obj: Any) -> 'InstructionSource': assert isinstance(obj, dict) content = from_str(obj.get("content")) id = from_str(obj.get("id")) label = from_str(obj.get("label")) - location = InstructionsSourcesLocation(obj.get("location")) + location = InstructionSourceLocation(obj.get("location")) source_path = from_str(obj.get("sourcePath")) - type = InstructionsSourcesType(obj.get("type")) + type = InstructionSourceType(obj.get("type")) apply_to = from_union([lambda x: from_list(from_str, x), from_none], obj.get("applyTo")) default_disabled = from_union([from_bool, from_none], obj.get("defaultDisabled")) description = from_union([from_str, from_none], obj.get("description")) - return InstructionsSources(content, id, label, location, source_path, type, apply_to, default_disabled, description) + project_path = from_union([from_str, from_none], obj.get("projectPath")) + return InstructionSource(content, id, label, location, source_path, type, apply_to, default_disabled, description, project_path) def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) result["id"] = from_str(self.id) result["label"] = from_str(self.label) - result["location"] = to_enum(InstructionsSourcesLocation, self.location) + result["location"] = to_enum(InstructionSourceLocation, self.location) result["sourcePath"] = from_str(self.source_path) - result["type"] = to_enum(InstructionsSourcesType, self.type) + result["type"] = to_enum(InstructionSourceType, self.type) if self.apply_to is not None: result["applyTo"] = from_union([lambda x: from_list(from_str, x), from_none], self.apply_to) if self.default_disabled is not None: result["defaultDisabled"] = from_union([from_bool, from_none], self.default_disabled) if self.description is not None: result["description"] = from_union([from_str, from_none], self.description) + if self.project_path is not None: + result["projectPath"] = from_union([from_str, from_none], self.project_path) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -13519,6 +13606,128 @@ def to_dict(self) -> dict: result["locationKey"] = from_str(self.location_key) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class TaskAgentInfo: + """Schema for the `TaskAgentInfo` type.""" + + agent_type: str + """Type of agent running this task""" + + description: str + """Short description of the task""" + + id: str + """Unique task identifier""" + + prompt: str + """Most recent prompt delivered to the agent. Updated whenever the agent receives a + follow-up message. + """ + started_at: datetime + """ISO 8601 timestamp when the task was started""" + + status: TaskStatus + """Current lifecycle status of the task""" + + tool_call_id: str + """Tool call ID associated with this agent task""" + + type: ClassVar[str] = "agent" + """Task kind""" + + active_started_at: datetime | None = None + """ISO 8601 timestamp when the current active period began""" + + active_time_ms: int | None = None + """Accumulated active execution time in milliseconds""" + + can_promote_to_background: bool | None = None + """Whether the task is currently in the original sync wait and can be moved to background + mode. False once it is already backgrounded, idle, finished, or no longer has a + promotable sync waiter. + """ + completed_at: datetime | None = None + """ISO 8601 timestamp when the task finished""" + + error: str | None = None + """Error message when the task failed""" + + execution_mode: TaskExecutionMode | None = None + """Whether task execution is synchronously awaited or managed in the background""" + + idle_since: datetime | None = None + """ISO 8601 timestamp when the agent entered idle state""" + + latest_response: str | None = None + """Most recent response text from the agent""" + + model: str | None = None + """Requested model override for the task when specified""" + + resolved_model: str | None = None + """Runtime model resolved for the task when available""" + + result: str | None = None + """Result text from the task when available""" + + @staticmethod + def from_dict(obj: Any) -> 'TaskAgentInfo': + assert isinstance(obj, dict) + agent_type = from_str(obj.get("agentType")) + description = from_str(obj.get("description")) + id = from_str(obj.get("id")) + prompt = from_str(obj.get("prompt")) + started_at = from_datetime(obj.get("startedAt")) + status = TaskStatus(obj.get("status")) + tool_call_id = from_str(obj.get("toolCallId")) + active_started_at = from_union([from_datetime, from_none], obj.get("activeStartedAt")) + active_time_ms = from_union([from_int, from_none], obj.get("activeTimeMs")) + can_promote_to_background = from_union([from_bool, from_none], obj.get("canPromoteToBackground")) + completed_at = from_union([from_datetime, from_none], obj.get("completedAt")) + error = from_union([from_str, from_none], obj.get("error")) + execution_mode = from_union([TaskExecutionMode, from_none], obj.get("executionMode")) + idle_since = from_union([from_datetime, from_none], obj.get("idleSince")) + latest_response = from_union([from_str, from_none], obj.get("latestResponse")) + model = from_union([from_str, from_none], obj.get("model")) + resolved_model = from_union([from_str, from_none], obj.get("resolvedModel")) + result = from_union([from_str, from_none], obj.get("result")) + return TaskAgentInfo(agent_type, description, id, prompt, started_at, status, tool_call_id, active_started_at, active_time_ms, can_promote_to_background, completed_at, error, execution_mode, idle_since, latest_response, model, resolved_model, result) + + def to_dict(self) -> dict: + result: dict = {} + result["agentType"] = from_str(self.agent_type) + result["description"] = from_str(self.description) + result["id"] = from_str(self.id) + result["prompt"] = from_str(self.prompt) + result["startedAt"] = self.started_at.isoformat() + result["status"] = to_enum(TaskStatus, self.status) + result["toolCallId"] = from_str(self.tool_call_id) + result["type"] = self.type + if self.active_started_at is not None: + result["activeStartedAt"] = from_union([lambda x: x.isoformat(), from_none], self.active_started_at) + if self.active_time_ms is not None: + result["activeTimeMs"] = from_union([from_int, from_none], self.active_time_ms) + if self.can_promote_to_background is not None: + result["canPromoteToBackground"] = from_union([from_bool, from_none], self.can_promote_to_background) + if self.completed_at is not None: + result["completedAt"] = from_union([lambda x: x.isoformat(), from_none], self.completed_at) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + if self.execution_mode is not None: + result["executionMode"] = from_union([lambda x: to_enum(TaskExecutionMode, x), from_none], self.execution_mode) + if self.idle_since is not None: + result["idleSince"] = from_union([lambda x: x.isoformat(), from_none], self.idle_since) + if self.latest_response is not None: + result["latestResponse"] = from_union([from_str, from_none], self.latest_response) + if self.model is not None: + result["model"] = from_union([from_str, from_none], self.model) + if self.resolved_model is not None: + result["resolvedModel"] = from_union([from_str, from_none], self.resolved_model) + if self.result is not None: + result["result"] = from_union([from_str, from_none], self.result) + return result + @dataclass class ToolList: """Built-in tools available for the requested model, with their parameters and instructions.""" @@ -14707,18 +14916,37 @@ def to_dict(self) -> dict: class InstructionsGetSourcesResult: """Instruction sources loaded for the session, in merge order.""" - sources: list[InstructionsSources] + sources: list[InstructionSource] """Instruction sources for the session""" @staticmethod def from_dict(obj: Any) -> 'InstructionsGetSourcesResult': assert isinstance(obj, dict) - sources = from_list(InstructionsSources.from_dict, obj.get("sources")) + sources = from_list(InstructionSource.from_dict, obj.get("sources")) return InstructionsGetSourcesResult(sources) def to_dict(self) -> dict: result: dict = {} - result["sources"] = from_list(lambda x: to_class(InstructionsSources, x), self.sources) + result["sources"] = from_list(lambda x: to_class(InstructionSource, x), self.sources) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ServerInstructionSourceList: + """Instruction sources discovered across user, repository, and plugin sources.""" + + sources: list[InstructionSource] + """All discovered instruction sources""" + + @staticmethod + def from_dict(obj: Any) -> 'ServerInstructionSourceList': + assert isinstance(obj, dict) + sources = from_list(InstructionSource.from_dict, obj.get("sources")) + return ServerInstructionSourceList(sources) + + def to_dict(self) -> dict: + result: dict = {} + result["sources"] = from_list(lambda x: to_class(InstructionSource, x), self.sources) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -15867,6 +16095,25 @@ def to_dict(self) -> dict: result["agent"] = to_class(AgentInfo, self.agent) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ServerAgentList: + """Agents discovered across user, project, plugin, and remote sources.""" + + agents: list[AgentInfo] + """All discovered agents across all sources""" + + @staticmethod + def from_dict(obj: Any) -> 'ServerAgentList': + assert isinstance(obj, dict) + agents = from_list(AgentInfo.from_dict, obj.get("agents")) + return ServerAgentList(agents) + + def to_dict(self) -> dict: + result: dict = {} + result["agents"] = from_list(lambda x: to_class(AgentInfo, x), self.agents) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TaskProgress: @@ -18975,122 +19222,6 @@ def to_dict(self) -> dict: result["taskType"] = from_union([lambda x: to_enum(TaskType, x), from_none], self.task_type) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TaskAgentInfo: - """Schema for the `TaskAgentInfo` type.""" - - agent_type: str - """Type of agent running this task""" - - description: str - """Short description of the task""" - - id: str - """Unique task identifier""" - - prompt: str - """Most recent prompt delivered to the agent. Updated whenever the agent receives a - follow-up message. - """ - started_at: datetime - """ISO 8601 timestamp when the task was started""" - - status: TaskStatus - """Current lifecycle status of the task""" - - tool_call_id: str - """Tool call ID associated with this agent task""" - - type: ClassVar[str] = "agent" - """Task kind""" - - active_started_at: datetime | None = None - """ISO 8601 timestamp when the current active period began""" - - active_time_ms: int | None = None - """Accumulated active execution time in milliseconds""" - - can_promote_to_background: bool | None = None - """Whether the task is currently in the original sync wait and can be moved to background - mode. False once it is already backgrounded, idle, finished, or no longer has a - promotable sync waiter. - """ - completed_at: datetime | None = None - """ISO 8601 timestamp when the task finished""" - - error: str | None = None - """Error message when the task failed""" - - execution_mode: TaskExecutionMode | None = None - """Whether task execution is synchronously awaited or managed in the background""" - - idle_since: datetime | None = None - """ISO 8601 timestamp when the agent entered idle state""" - - latest_response: str | None = None - """Most recent response text from the agent""" - - model: str | None = None - """Model used for the task when specified""" - - result: str | None = None - """Result text from the task when available""" - - @staticmethod - def from_dict(obj: Any) -> 'TaskAgentInfo': - assert isinstance(obj, dict) - agent_type = from_str(obj.get("agentType")) - description = from_str(obj.get("description")) - id = from_str(obj.get("id")) - prompt = from_str(obj.get("prompt")) - started_at = from_datetime(obj.get("startedAt")) - status = TaskStatus(obj.get("status")) - tool_call_id = from_str(obj.get("toolCallId")) - active_started_at = from_union([from_datetime, from_none], obj.get("activeStartedAt")) - active_time_ms = from_union([from_int, from_none], obj.get("activeTimeMs")) - can_promote_to_background = from_union([from_bool, from_none], obj.get("canPromoteToBackground")) - completed_at = from_union([from_datetime, from_none], obj.get("completedAt")) - error = from_union([from_str, from_none], obj.get("error")) - execution_mode = from_union([TaskExecutionMode, from_none], obj.get("executionMode")) - idle_since = from_union([from_datetime, from_none], obj.get("idleSince")) - latest_response = from_union([from_str, from_none], obj.get("latestResponse")) - model = from_union([from_str, from_none], obj.get("model")) - result = from_union([from_str, from_none], obj.get("result")) - return TaskAgentInfo(agent_type, description, id, prompt, started_at, status, tool_call_id, active_started_at, active_time_ms, can_promote_to_background, completed_at, error, execution_mode, idle_since, latest_response, model, result) - - def to_dict(self) -> dict: - result: dict = {} - result["agentType"] = from_str(self.agent_type) - result["description"] = from_str(self.description) - result["id"] = from_str(self.id) - result["prompt"] = from_str(self.prompt) - result["startedAt"] = self.started_at.isoformat() - result["status"] = to_enum(TaskStatus, self.status) - result["toolCallId"] = from_str(self.tool_call_id) - result["type"] = self.type - if self.active_started_at is not None: - result["activeStartedAt"] = from_union([lambda x: x.isoformat(), from_none], self.active_started_at) - if self.active_time_ms is not None: - result["activeTimeMs"] = from_union([from_int, from_none], self.active_time_ms) - if self.can_promote_to_background is not None: - result["canPromoteToBackground"] = from_union([from_bool, from_none], self.can_promote_to_background) - if self.completed_at is not None: - result["completedAt"] = from_union([lambda x: x.isoformat(), from_none], self.completed_at) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) - if self.execution_mode is not None: - result["executionMode"] = from_union([lambda x: to_enum(TaskExecutionMode, x), from_none], self.execution_mode) - if self.idle_since is not None: - result["idleSince"] = from_union([lambda x: x.isoformat(), from_none], self.idle_since) - if self.latest_response is not None: - result["latestResponse"] = from_union([from_str, from_none], self.latest_response) - if self.model is not None: - result["model"] = from_union([from_str, from_none], self.model) - if self.result is not None: - result["result"] = from_union([from_str, from_none], self.result) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ToolsGetCurrentMetadataResult: @@ -19176,6 +19307,7 @@ class RPC: agent_registry_spawn_validation_error_field: AgentRegistrySpawnValidationErrorField agent_registry_spawn_validation_error_reason: AgentRegistrySpawnValidationErrorReason agent_reload_result: AgentReloadResult + agents_discover_request: AgentsDiscoverRequest agent_select_request: AgentSelectRequest agent_select_result: AgentSelectResult allow_all_permission_set_result: AllowAllPermissionSetResult @@ -19284,10 +19416,11 @@ class RPC: installed_plugin_source_git_hub: InstalledPluginSourceGitHub installed_plugin_source_local: InstalledPluginSourceLocal installed_plugin_source_url: InstalledPluginSourceURL + instructions_discover_request: InstructionsDiscoverRequest instructions_get_sources_result: InstructionsGetSourcesResult - instructions_sources: InstructionsSources - instructions_sources_location: InstructionsSourcesLocation - instructions_sources_type: InstructionsSourcesType + instruction_source: InstructionSource + instruction_source_location: InstructionSourceLocation + instruction_source_type: InstructionSourceType local_session_metadata_value: LocalSessionMetadataValue log_request: LogRequest log_result: LogResult @@ -19608,6 +19741,8 @@ class RPC: send_mode: SendMode send_request: SendRequest send_result: SendResult + server_agent_list: ServerAgentList + server_instruction_source_list: ServerInstructionSourceList server_skill: ServerSkill server_skill_list: ServerSkillList session_activity: SessionActivity @@ -19885,6 +20020,7 @@ def from_dict(obj: Any) -> 'RPC': agent_registry_spawn_validation_error_field = AgentRegistrySpawnValidationErrorField(obj.get("AgentRegistrySpawnValidationErrorField")) agent_registry_spawn_validation_error_reason = AgentRegistrySpawnValidationErrorReason(obj.get("AgentRegistrySpawnValidationErrorReason")) agent_reload_result = AgentReloadResult.from_dict(obj.get("AgentReloadResult")) + agents_discover_request = AgentsDiscoverRequest.from_dict(obj.get("AgentsDiscoverRequest")) agent_select_request = AgentSelectRequest.from_dict(obj.get("AgentSelectRequest")) agent_select_result = AgentSelectResult.from_dict(obj.get("AgentSelectResult")) allow_all_permission_set_result = AllowAllPermissionSetResult.from_dict(obj.get("AllowAllPermissionSetResult")) @@ -19993,10 +20129,11 @@ def from_dict(obj: Any) -> 'RPC': installed_plugin_source_git_hub = InstalledPluginSourceGitHub.from_dict(obj.get("InstalledPluginSourceGitHub")) installed_plugin_source_local = InstalledPluginSourceLocal.from_dict(obj.get("InstalledPluginSourceLocal")) installed_plugin_source_url = InstalledPluginSourceURL.from_dict(obj.get("InstalledPluginSourceUrl")) + instructions_discover_request = InstructionsDiscoverRequest.from_dict(obj.get("InstructionsDiscoverRequest")) instructions_get_sources_result = InstructionsGetSourcesResult.from_dict(obj.get("InstructionsGetSourcesResult")) - instructions_sources = InstructionsSources.from_dict(obj.get("InstructionsSources")) - instructions_sources_location = InstructionsSourcesLocation(obj.get("InstructionsSourcesLocation")) - instructions_sources_type = InstructionsSourcesType(obj.get("InstructionsSourcesType")) + instruction_source = InstructionSource.from_dict(obj.get("InstructionSource")) + instruction_source_location = InstructionSourceLocation(obj.get("InstructionSourceLocation")) + instruction_source_type = InstructionSourceType(obj.get("InstructionSourceType")) local_session_metadata_value = LocalSessionMetadataValue.from_dict(obj.get("LocalSessionMetadataValue")) log_request = LogRequest.from_dict(obj.get("LogRequest")) log_result = LogResult.from_dict(obj.get("LogResult")) @@ -20317,6 +20454,8 @@ def from_dict(obj: Any) -> 'RPC': send_mode = SendMode(obj.get("SendMode")) send_request = SendRequest.from_dict(obj.get("SendRequest")) send_result = SendResult.from_dict(obj.get("SendResult")) + server_agent_list = ServerAgentList.from_dict(obj.get("ServerAgentList")) + server_instruction_source_list = ServerInstructionSourceList.from_dict(obj.get("ServerInstructionSourceList")) server_skill = ServerSkill.from_dict(obj.get("ServerSkill")) server_skill_list = ServerSkillList.from_dict(obj.get("ServerSkillList")) session_activity = SessionActivity.from_dict(obj.get("SessionActivity")) @@ -20564,7 +20703,7 @@ def from_dict(obj: Any) -> 'RPC': session_context_info = from_union([SessionContextInfo.from_dict, from_none], obj.get("SessionContextInfo")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agent_select_request, agent_select_result, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_instance_availability, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_config, provider_config_azure, provider_config_type, provider_config_wire_api, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_instance_availability, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instructions_discover_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_config, provider_config_azure, provider_config_type, provider_config_wire_api, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -20594,6 +20733,7 @@ def to_dict(self) -> dict: result["AgentRegistrySpawnValidationErrorField"] = to_enum(AgentRegistrySpawnValidationErrorField, self.agent_registry_spawn_validation_error_field) result["AgentRegistrySpawnValidationErrorReason"] = to_enum(AgentRegistrySpawnValidationErrorReason, self.agent_registry_spawn_validation_error_reason) result["AgentReloadResult"] = to_class(AgentReloadResult, self.agent_reload_result) + result["AgentsDiscoverRequest"] = to_class(AgentsDiscoverRequest, self.agents_discover_request) result["AgentSelectRequest"] = to_class(AgentSelectRequest, self.agent_select_request) result["AgentSelectResult"] = to_class(AgentSelectResult, self.agent_select_result) result["AllowAllPermissionSetResult"] = to_class(AllowAllPermissionSetResult, self.allow_all_permission_set_result) @@ -20702,10 +20842,11 @@ def to_dict(self) -> dict: result["InstalledPluginSourceGitHub"] = to_class(InstalledPluginSourceGitHub, self.installed_plugin_source_git_hub) result["InstalledPluginSourceLocal"] = to_class(InstalledPluginSourceLocal, self.installed_plugin_source_local) result["InstalledPluginSourceUrl"] = to_class(InstalledPluginSourceURL, self.installed_plugin_source_url) + result["InstructionsDiscoverRequest"] = to_class(InstructionsDiscoverRequest, self.instructions_discover_request) result["InstructionsGetSourcesResult"] = to_class(InstructionsGetSourcesResult, self.instructions_get_sources_result) - result["InstructionsSources"] = to_class(InstructionsSources, self.instructions_sources) - result["InstructionsSourcesLocation"] = to_enum(InstructionsSourcesLocation, self.instructions_sources_location) - result["InstructionsSourcesType"] = to_enum(InstructionsSourcesType, self.instructions_sources_type) + result["InstructionSource"] = to_class(InstructionSource, self.instruction_source) + result["InstructionSourceLocation"] = to_enum(InstructionSourceLocation, self.instruction_source_location) + result["InstructionSourceType"] = to_enum(InstructionSourceType, self.instruction_source_type) result["LocalSessionMetadataValue"] = to_class(LocalSessionMetadataValue, self.local_session_metadata_value) result["LogRequest"] = to_class(LogRequest, self.log_request) result["LogResult"] = to_class(LogResult, self.log_result) @@ -21026,6 +21167,8 @@ def to_dict(self) -> dict: result["SendMode"] = to_enum(SendMode, self.send_mode) result["SendRequest"] = to_class(SendRequest, self.send_request) result["SendResult"] = to_class(SendResult, self.send_result) + result["ServerAgentList"] = to_class(ServerAgentList, self.server_agent_list) + result["ServerInstructionSourceList"] = to_class(ServerInstructionSourceList, self.server_instruction_source_list) result["ServerSkill"] = to_class(ServerSkill, self.server_skill) result["ServerSkillList"] = to_class(ServerSkillList, self.server_skill_list) result["SessionActivity"] = to_class(SessionActivity, self.session_activity) @@ -21737,6 +21880,28 @@ async def discover(self, params: SkillsDiscoverRequest, *, timeout: float | None return ServerSkillList.from_dict(await self._client.request("skills.discover", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class ServerAgentsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def discover(self, params: AgentsDiscoverRequest, *, timeout: float | None = None) -> ServerAgentList: + "Discovers custom agents across user, project, plugin, and remote sources.\n\nArgs:\n params: Optional project paths to include in agent discovery.\n\nReturns:\n Agents discovered across user, project, plugin, and remote sources." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ServerAgentList.from_dict(await self._client.request("agents.discover", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class ServerInstructionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def discover(self, params: InstructionsDiscoverRequest, *, timeout: float | None = None) -> ServerInstructionSourceList: + "Discovers instruction sources across user, repository, and plugin sources.\n\nArgs:\n params: Optional project paths to include in instruction discovery.\n\nReturns:\n Instruction sources discovered across user, repository, and plugin sources." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return ServerInstructionSourceList.from_dict(await self._client.request("instructions.discover", params_dict, **_timeout_kwargs(timeout))) + + class ServerUserSettingsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -21912,6 +22077,8 @@ def __init__(self, client: "JsonRpcClient"): self.mcp = ServerMcpApi(client) self.plugins = ServerPluginsApi(client) self.skills = ServerSkillsApi(client) + self.agents = ServerAgentsApi(client) + self.instructions = ServerInstructionsApi(client) self.user = ServerUserApi(client) self.runtime = ServerRuntimeApi(client) self.session_fs = ServerSessionFsApi(client) @@ -22330,12 +22497,6 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id - async def respond(self, params: MCPOauthRespondRequest, *, timeout: float | None = None) -> MCPOauthRespondResult: - "Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK.\n\nArgs:\n params: MCP OAuth request id and optional provider response.\n\nReturns:\n Empty result after recording the MCP OAuth response." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - return MCPOauthRespondResult.from_dict(await self._client.request("session.mcp.oauth.respond", params_dict, **_timeout_kwargs(timeout))) - async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = None) -> MCPOauthLoginResult: "Starts OAuth authentication for a remote MCP server.\n\nArgs:\n params: Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy.\n\nReturns:\n OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22418,12 +22579,6 @@ async def reload(self, *, timeout: float | None = None) -> None: "Reloads MCP server connections for the session." await self._client.request("session.mcp.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) - async def reload_with_config(self, params: MCPReloadWithConfigRequest, *, timeout: float | None = None) -> MCPStartServersResult: - "Reloads MCP server connections for the session with an explicit host-provided configuration.\n\nArgs:\n params: Opaque MCP reload configuration.\n\nReturns:\n MCP server startup filtering result." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - return MCPStartServersResult.from_dict(await self._client.request("session.mcp.reloadWithConfig", params_dict, **_timeout_kwargs(timeout))) - async def execute_sampling(self, params: MCPExecuteSamplingParams, *, timeout: float | None = None) -> MCPSamplingExecutionResult: "Runs an MCP sampling inference on behalf of an MCP server.\n\nArgs:\n params: Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference.\n\nReturns:\n Outcome of an MCP sampling execution: success result, failure error, or cancellation." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22446,42 +22601,12 @@ async def remove_git_hub(self, *, timeout: float | None = None) -> MCPRemoveGitH "Removes the auto-managed `github` MCP server when present.\n\nReturns:\n Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove)." return MCPRemoveGitHubResult.from_dict(await self._client.request("session.mcp.removeGitHub", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) - async def configure_git_hub(self, params: MCPConfigureGitHubRequest, *, timeout: float | None = None) -> MCPConfigureGitHubResult: - "Configures the built-in GitHub MCP server for the session's current auth context.\n\nArgs:\n params: Opaque auth info used to configure GitHub MCP.\n\nReturns:\n Result of configuring GitHub MCP." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - return MCPConfigureGitHubResult.from_dict(await self._client.request("session.mcp.configureGitHub", params_dict, **_timeout_kwargs(timeout))) - - async def start_server(self, params: MCPStartServerRequest, *, timeout: float | None = None) -> None: - "Starts an individual MCP server on the session's host.\n\nArgs:\n params: Server name and opaque configuration for an individual MCP server start." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - await self._client.request("session.mcp.startServer", params_dict, **_timeout_kwargs(timeout)) - - async def restart_server(self, params: MCPRestartServerRequest, *, timeout: float | None = None) -> None: - "Restarts an individual MCP server on the session's host (stops then starts).\n\nArgs:\n params: Server name and opaque configuration for an individual MCP server restart." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - await self._client.request("session.mcp.restartServer", params_dict, **_timeout_kwargs(timeout)) - async def stop_server(self, params: MCPStopServerRequest, *, timeout: float | None = None) -> None: "Stops an individual MCP server on the session's host.\n\nArgs:\n params: Server name for an individual MCP server stop." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.stopServer", params_dict, **_timeout_kwargs(timeout)) - async def register_external_client(self, params: MCPRegisterExternalClientRequest, *, timeout: float | None = None) -> None: - "Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself.\n\nArgs:\n params: Registration parameters for an external MCP client." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - await self._client.request("session.mcp.registerExternalClient", params_dict, **_timeout_kwargs(timeout)) - - async def unregister_external_client(self, params: MCPUnregisterExternalClientRequest, *, timeout: float | None = None) -> None: - "Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime.\n\nArgs:\n params: Server name identifying the external client to remove." - params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} - params_dict["sessionId"] = self._session_id - await self._client.request("session.mcp.unregisterExternalClient", params_dict, **_timeout_kwargs(timeout)) - async def is_server_running(self, params: MCPIsServerRunningRequest, *, timeout: float | None = None) -> MCPIsServerRunningResult: "Checks whether a named MCP server is currently running on the session's host.\n\nArgs:\n params: Server name to check running status for.\n\nReturns:\n Whether the named MCP server is running." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23131,6 +23256,71 @@ async def log(self, params: LogRequest, *, timeout: float | None = None) -> LogR return LogResult.from_dict(await self._client.request("session.log", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class _InternalMcpOauthApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + + async def _respond(self, params: MCPOauthRespondRequest, *, timeout: float | None = None) -> MCPOauthRespondResult: + "Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK.\n\nArgs:\n params: MCP OAuth request id and optional provider response.\n\nReturns:\n Empty result after recording the MCP OAuth response.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return MCPOauthRespondResult.from_dict(await self._client.request("session.mcp.oauth.respond", params_dict, **_timeout_kwargs(timeout))) + + +# Experimental: this API group is experimental and may change or be removed. +class _InternalMcpApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + self.oauth = _InternalMcpOauthApi(client, session_id) + + async def _reload_with_config(self, params: MCPReloadWithConfigRequest, *, timeout: float | None = None) -> MCPStartServersResult: + "Reloads MCP server connections for the session with an explicit host-provided configuration.\n\nArgs:\n params: Opaque MCP reload configuration.\n\nReturns:\n MCP server startup filtering result.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return MCPStartServersResult.from_dict(await self._client.request("session.mcp.reloadWithConfig", params_dict, **_timeout_kwargs(timeout))) + + async def _configure_git_hub(self, params: MCPConfigureGitHubRequest, *, timeout: float | None = None) -> MCPConfigureGitHubResult: + "Configures the built-in GitHub MCP server for the session's current auth context.\n\nArgs:\n params: Opaque auth info used to configure GitHub MCP.\n\nReturns:\n Result of configuring GitHub MCP.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return MCPConfigureGitHubResult.from_dict(await self._client.request("session.mcp.configureGitHub", params_dict, **_timeout_kwargs(timeout))) + + async def _start_server(self, params: MCPStartServerRequest, *, timeout: float | None = None) -> None: + "Starts an individual MCP server on the session's host.\n\nArgs:\n params: Server name and opaque configuration for an individual MCP server start.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.mcp.startServer", params_dict, **_timeout_kwargs(timeout)) + + async def _restart_server(self, params: MCPRestartServerRequest, *, timeout: float | None = None) -> None: + "Restarts an individual MCP server on the session's host (stops then starts).\n\nArgs:\n params: Server name and opaque configuration for an individual MCP server restart.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.mcp.restartServer", params_dict, **_timeout_kwargs(timeout)) + + async def _register_external_client(self, params: MCPRegisterExternalClientRequest, *, timeout: float | None = None) -> None: + "Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself.\n\nArgs:\n params: Registration parameters for an external MCP client.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.mcp.registerExternalClient", params_dict, **_timeout_kwargs(timeout)) + + async def _unregister_external_client(self, params: MCPUnregisterExternalClientRequest, *, timeout: float | None = None) -> None: + "Unregisters a previously registered external MCP client by server name. Marked internal as the paired companion of `registerExternalClient`: only in-process callers that registered a client this way can meaningfully unregister it. Disappears alongside `registerExternalClient`: once external clients are described to the runtime as config rather than handed in as instances, lifecycle (including deregistration) is owned entirely by the runtime.\n\nArgs:\n params: Server name identifying the external client to remove.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.mcp.unregisterExternalClient", params_dict, **_timeout_kwargs(timeout)) + + +class _InternalSessionRpc: + """Internal SDK session-scoped RPC methods. Not part of the public API.""" + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + self.mcp = _InternalMcpApi(client, session_id) + + # Experimental: this API group is experimental and may change or be removed. class SessionFsHandler(Protocol): async def read_file(self, params: SessionFSReadFileRequest) -> SessionFSReadFileResult: @@ -23336,6 +23526,7 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "AgentReloadResult", "AgentSelectRequest", "AgentSelectResult", + "AgentsDiscoverRequest", "AllowAllPermissionSetResult", "AllowAllPermissionState", "ApprovalKind", @@ -23463,11 +23654,12 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "InstalledPluginSourceGitHub", "InstalledPluginSourceLocal", "InstalledPluginSourceURL", + "InstructionSource", + "InstructionSourceLocation", + "InstructionSourceType", "InstructionsApi", + "InstructionsDiscoverRequest", "InstructionsGetSourcesResult", - "InstructionsSources", - "InstructionsSourcesLocation", - "InstructionsSourcesType", "KindEnum", "LocalSessionMetadataValue", "LogRequest", @@ -23848,7 +24040,11 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "SendRequest", "SendResult", "ServerAccountApi", + "ServerAgentList", "ServerAgentRegistryApi", + "ServerAgentsApi", + "ServerInstructionSourceList", + "ServerInstructionsApi", "ServerMcpApi", "ServerMcpConfigApi", "ServerModelsApi", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 332ac3769..f2e155f49 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -2032,18 +2032,23 @@ def to_dict(self) -> dict: class HookProgressData: "Ephemeral progress update from a running hook process" message: str + temporary: bool | None = None @staticmethod def from_dict(obj: Any) -> "HookProgressData": assert isinstance(obj, dict) message = from_str(obj.get("message")) + temporary = from_union([from_none, from_bool], obj.get("temporary")) return HookProgressData( message=message, + temporary=temporary, ) def to_dict(self) -> dict: result: dict = {} result["message"] = from_str(self.message) + if self.temporary is not None: + result["temporary"] = from_union([from_none, from_bool], self.temporary) return result @@ -4330,6 +4335,7 @@ class SessionResumeData: context: WorkingDirectoryContext | None = None context_tier: ContextTier | None = None continue_pending_work: bool | None = None + events_file_size_bytes: int | None = None reasoning_effort: str | None = None reasoning_summary: ReasoningSummary | None = None remote_steerable: bool | None = None @@ -4345,6 +4351,7 @@ def from_dict(obj: Any) -> "SessionResumeData": context = from_union([from_none, WorkingDirectoryContext.from_dict], obj.get("context")) context_tier = from_union([from_none, lambda x: parse_enum(ContextTier, x)], obj.get("contextTier")) continue_pending_work = from_union([from_none, from_bool], obj.get("continuePendingWork")) + events_file_size_bytes = from_union([from_none, from_int], obj.get("eventsFileSizeBytes")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) reasoning_summary = from_union([from_none, lambda x: parse_enum(ReasoningSummary, x)], obj.get("reasoningSummary")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) @@ -4357,6 +4364,7 @@ def from_dict(obj: Any) -> "SessionResumeData": context=context, context_tier=context_tier, continue_pending_work=continue_pending_work, + events_file_size_bytes=events_file_size_bytes, reasoning_effort=reasoning_effort, reasoning_summary=reasoning_summary, remote_steerable=remote_steerable, @@ -4376,6 +4384,8 @@ def to_dict(self) -> dict: result["contextTier"] = from_union([from_none, lambda x: to_enum(ContextTier, x)], self.context_tier) if self.continue_pending_work is not None: result["continuePendingWork"] = from_union([from_none, from_bool], self.continue_pending_work) + if self.events_file_size_bytes is not None: + result["eventsFileSizeBytes"] = from_union([from_none, to_int], self.events_file_size_bytes) if self.reasoning_effort is not None: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.reasoning_summary is not None: @@ -4412,36 +4422,52 @@ def to_dict(self) -> dict: class SessionScheduleCreatedData: "Scheduled prompt registered via /every or /after" id: int - interval: timedelta prompt: str + at: int | None = None + cron: str | None = None display_prompt: str | None = None + interval: timedelta | None = None recurring: bool | None = None + tz: str | None = None @staticmethod def from_dict(obj: Any) -> "SessionScheduleCreatedData": assert isinstance(obj, dict) id = from_int(obj.get("id")) - interval = from_timedelta(obj.get("intervalMs")) prompt = from_str(obj.get("prompt")) + at = from_union([from_none, from_int], obj.get("at")) + cron = from_union([from_none, from_str], obj.get("cron")) display_prompt = from_union([from_none, from_str], obj.get("displayPrompt")) + interval = from_union([from_none, from_timedelta], obj.get("intervalMs")) recurring = from_union([from_none, from_bool], obj.get("recurring")) + tz = from_union([from_none, from_str], obj.get("tz")) return SessionScheduleCreatedData( id=id, - interval=interval, prompt=prompt, + at=at, + cron=cron, display_prompt=display_prompt, + interval=interval, recurring=recurring, + tz=tz, ) def to_dict(self) -> dict: result: dict = {} result["id"] = to_int(self.id) - result["intervalMs"] = to_timedelta_int(self.interval) result["prompt"] = from_str(self.prompt) + if self.at is not None: + result["at"] = from_union([from_none, to_int], self.at) + if self.cron is not None: + result["cron"] = from_union([from_none, from_str], self.cron) if self.display_prompt is not None: result["displayPrompt"] = from_union([from_none, from_str], self.display_prompt) + if self.interval is not None: + result["intervalMs"] = from_union([from_none, to_timedelta_int], self.interval) if self.recurring is not None: result["recurring"] = from_union([from_none, from_bool], self.recurring) + if self.tz is not None: + result["tz"] = from_union([from_none, from_str], self.tz) return result @@ -4457,6 +4483,7 @@ class SessionShutdownData: current_model: str | None = None current_tokens: int | None = None error_reason: str | None = None + events_file_size_bytes: int | None = None system_tokens: int | None = None token_details: dict[str, ShutdownTokenDetail] | None = None tool_definitions_tokens: int | None = None @@ -4477,6 +4504,7 @@ def from_dict(obj: Any) -> "SessionShutdownData": current_model = from_union([from_none, from_str], obj.get("currentModel")) current_tokens = from_union([from_none, from_int], obj.get("currentTokens")) error_reason = from_union([from_none, from_str], obj.get("errorReason")) + events_file_size_bytes = from_union([from_none, from_int], obj.get("eventsFileSizeBytes")) system_tokens = from_union([from_none, from_int], obj.get("systemTokens")) token_details = from_union([from_none, lambda x: from_dict(ShutdownTokenDetail.from_dict, x)], obj.get("tokenDetails")) tool_definitions_tokens = from_union([from_none, from_int], obj.get("toolDefinitionsTokens")) @@ -4492,6 +4520,7 @@ def from_dict(obj: Any) -> "SessionShutdownData": current_model=current_model, current_tokens=current_tokens, error_reason=error_reason, + events_file_size_bytes=events_file_size_bytes, system_tokens=system_tokens, token_details=token_details, tool_definitions_tokens=tool_definitions_tokens, @@ -4514,6 +4543,8 @@ def to_dict(self) -> dict: result["currentTokens"] = from_union([from_none, to_int], self.current_tokens) if self.error_reason is not None: result["errorReason"] = from_union([from_none, from_str], self.error_reason) + if self.events_file_size_bytes is not None: + result["eventsFileSizeBytes"] = from_union([from_none, to_int], self.events_file_size_bytes) if self.system_tokens is not None: result["systemTokens"] = from_union([from_none, to_int], self.system_tokens) if self.token_details is not None: diff --git a/python/e2e/test_abort_e2e.py b/python/e2e/test_abort_e2e.py index 6711fb114..ce3a497f4 100644 --- a/python/e2e/test_abort_e2e.py +++ b/python/e2e/test_abort_e2e.py @@ -57,10 +57,25 @@ def on_event(event): types = [e.type.value for e in events] assert "assistant.message_delta" in types - # Session should be in a usable state after abort - follow_up = await session.send_and_wait("Say 'abort_recovery_ok'.", timeout=60.0) - assert follow_up is not None - assert "abort_recovery_ok" in (follow_up.data.content or "").lower() + # Session should be usable after abort. Wait for the specific recovery + # message rather than racing against a late idle from the aborted turn. + recovery_received: asyncio.Future = asyncio.get_event_loop().create_future() + + def check_recovery(event): + if ( + event.type.value == "assistant.message" + and "abort_recovery_ok" in (event.data.content or "").lower() + and not recovery_received.done() + ): + recovery_received.set_result(event) + + unsubscribe_recovery = session.on(check_recovery) + try: + await session.send("Say 'abort_recovery_ok'.") + recovery_message = await asyncio.wait_for(recovery_received, timeout=60.0) + assert "abort_recovery_ok" in (recovery_message.data.content or "").lower() + finally: + unsubscribe_recovery() finally: unsubscribe() await session.disconnect() diff --git a/python/e2e/test_rpc_mcp_lifecycle_e2e.py b/python/e2e/test_rpc_mcp_lifecycle_e2e.py index 090a3b07c..a16603706 100644 --- a/python/e2e/test_rpc_mcp_lifecycle_e2e.py +++ b/python/e2e/test_rpc_mcp_lifecycle_e2e.py @@ -13,16 +13,9 @@ import pytest from copilot.rpc import ( - MCPConfigureGitHubRequest, MCPIsServerRunningRequest, MCPListToolsRequest, - MCPOauthRespondRequest, - MCPRegisterExternalClientRequest, - MCPReloadWithConfigRequest, - MCPRestartServerRequest, - MCPStartServerRequest, MCPStopServerRequest, - MCPUnregisterExternalClientRequest, ) from copilot.session import PermissionHandler from copilot.session_events import McpServerStatus @@ -49,15 +42,6 @@ def _test_mcp_servers(*server_names: str) -> dict[str, dict]: } -def _wire_mcp_server_config() -> dict: - return { - "command": "node", - "args": [TEST_MCP_SERVER], - "tools": ["*"], - "cwd": TEST_HARNESS_DIR, - } - - async def _wait_for_mcp_server_status( session, server_name: str, @@ -165,115 +149,3 @@ async def test_should_stop_running_mcp_server(self, ctx: E2ETestContext): await session.rpc.mcp.stop_server(MCPStopServerRequest(server_name=server_name)) await _wait_for_mcp_running(session, server_name, expected_running=False) - - async def test_should_start_and_restart_mcp_server(self, ctx: E2ETestContext): - host_server = "rpc-lifecycle-host-server" - async with await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=_test_mcp_servers(host_server), - ) as session: - await _wait_for_mcp_server_status(session, host_server) - - started_server = "rpc-lifecycle-started-server" - config = _wire_mcp_server_config() - - await session.rpc.mcp.start_server( - MCPStartServerRequest(server_name=started_server, config=config) - ) - await _wait_for_mcp_running(session, started_server, expected_running=True) - - tools = await session.rpc.mcp.list_tools( - MCPListToolsRequest(server_name=started_server) - ) - assert len(tools.tools) > 0 - - await session.rpc.mcp.restart_server( - MCPRestartServerRequest(server_name=started_server, config=config) - ) - await _wait_for_mcp_running(session, started_server, expected_running=True) - - async def test_should_register_and_unregister_external_mcp_client(self, ctx: E2ETestContext): - host_server = "rpc-lifecycle-extclient-host" - async with await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=_test_mcp_servers(host_server), - ) as session: - await _wait_for_mcp_server_status(session, host_server) - - external_name = "rpc-lifecycle-external-client" - assert ( - await session.rpc.mcp.is_server_running( - MCPIsServerRunningRequest(server_name=external_name) - ) - ).running is False - - await session.rpc.mcp.register_external_client( - MCPRegisterExternalClientRequest( - server_name=external_name, - client={"id": external_name}, - transport={"kind": "in-process"}, - config={"command": "noop"}, - ) - ) - assert ( - await session.rpc.mcp.is_server_running( - MCPIsServerRunningRequest(server_name=external_name) - ) - ).running is True - - await session.rpc.mcp.unregister_external_client( - MCPUnregisterExternalClientRequest(server_name=external_name) - ) - assert ( - await session.rpc.mcp.is_server_running( - MCPIsServerRunningRequest(server_name=external_name) - ) - ).running is False - - async def test_should_reload_mcp_servers_with_config(self, ctx: E2ETestContext): - host_server = "rpc-lifecycle-reload-host" - async with await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=_test_mcp_servers(host_server), - ) as session: - await _wait_for_mcp_server_status(session, host_server) - - result = await session.rpc.mcp.reload_with_config( - MCPReloadWithConfigRequest( - config={"mcpServers": {}, "disabledServers": []}, - ) - ) - - assert result is not None - assert result.filtered_servers is not None - assert result.filtered_servers == [] - - async def test_should_configure_github_mcp_server(self, ctx: E2ETestContext): - host_server = "rpc-lifecycle-configure-host" - async with await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=_test_mcp_servers(host_server), - ) as session: - await _wait_for_mcp_server_status(session, host_server) - - result = await session.rpc.mcp.configure_git_hub( - MCPConfigureGitHubRequest(auth_info={"type": "api-key"}) - ) - - assert result is not None - assert result.changed is False - - async def test_should_respond_to_mcp_oauth_request_without_pending_request( - self, ctx: E2ETestContext - ): - host_server = "rpc-lifecycle-oauth-host" - async with await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=_test_mcp_servers(host_server), - ) as session: - await _wait_for_mcp_server_status(session, host_server) - - result = await session.rpc.mcp.oauth.respond( - MCPOauthRespondRequest(request_id=f"missing-{uuid.uuid4().hex}") - ) - assert result is not None diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 57aa9fcbd..f52c06651 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -72,6 +72,10 @@ pub mod rpc_methods { pub const SKILLS_CONFIG_SETDISABLEDSKILLS: &str = "skills.config.setDisabledSkills"; /// `skills.discover` pub const SKILLS_DISCOVER: &str = "skills.discover"; + /// `agents.discover` + pub const AGENTS_DISCOVER: &str = "agents.discover"; + /// `instructions.discover` + pub const INSTRUCTIONS_DISCOVER: &str = "instructions.discover"; /// `user.settings.reload` pub const USER_SETTINGS_RELOAD: &str = "user.settings.reload"; /// `runtime.shutdown` @@ -880,6 +884,25 @@ pub struct AgentReloadResult { pub agents: Vec, } +/// Optional project paths to include in agent discovery. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentsDiscoverRequest { + /// When true, omit the host's agents (the `/agents` directory and all plugin agents), leaving only project and remote agents. For multitenant deployments. + #[serde(skip_serializing_if = "Option::is_none")] + pub exclude_host_agents: Option, + /// Optional list of project directory paths to scan for project-scoped agents. When omitted or empty, only user/plugin/remote-independent agents are returned (no project scan). + #[serde(skip_serializing_if = "Option::is_none")] + pub project_paths: Option>, +} + /// Name of the custom agent to select for subsequent turns. /// ///
@@ -3060,7 +3083,7 @@ pub struct InstalledPluginSourceUrl { pub url: String, } -/// Schema for the `InstructionsSources` type. +/// Optional project paths to include in instruction discovery. /// ///
/// @@ -3070,7 +3093,26 @@ pub struct InstalledPluginSourceUrl { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct InstructionsSources { +pub struct InstructionsDiscoverRequest { + /// When true, omit the host's instruction sources (user/home-level files and plugin rules), leaving only repository and working-directory sources. For multitenant deployments. + #[serde(skip_serializing_if = "Option::is_none")] + pub exclude_host_instructions: Option, + /// Optional list of project directory paths to scan for repository/working-directory instruction sources. When omitted or empty, only user-level and plugin instruction sources are returned (no project scan). + #[serde(skip_serializing_if = "Option::is_none")] + pub project_paths: Option>, +} + +/// Schema for the `InstructionSource` type. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InstructionSource { /// Glob pattern(s) from frontmatter — when set, this instruction applies only to matching files #[serde(skip_serializing_if = "Option::is_none")] pub apply_to: Option>, @@ -3087,11 +3129,14 @@ pub struct InstructionsSources { /// Human-readable label pub label: String, /// Where this source lives — used for UI grouping - pub location: InstructionsSourcesLocation, + pub location: InstructionSourceLocation, + /// The project path this source was discovered from. Only set by sessionless discovery for repository/working-directory sources, where it disambiguates same-named files (e.g. .github/copilot-instructions.md) across multiple workspace roots. The session-scoped getSources leaves it unset. + #[serde(skip_serializing_if = "Option::is_none")] + pub project_path: Option, /// File path relative to repo or absolute for home pub source_path: String, /// Category of instruction source — used for merge logic - pub r#type: InstructionsSourcesType, + pub r#type: InstructionSourceType, } /// Instruction sources loaded for the session, in merge order. @@ -3106,7 +3151,7 @@ pub struct InstructionsSources { #[serde(rename_all = "camelCase")] pub struct InstructionsGetSourcesResult { /// Instruction sources for the session - pub sources: Vec, + pub sources: Vec, } /// Pre-resolved working-directory context for session startup. @@ -7925,19 +7970,29 @@ pub struct SandboxConfig { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ScheduleEntry { + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + #[serde(skip_serializing_if = "Option::is_none")] + pub at: Option, + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + #[serde(skip_serializing_if = "Option::is_none")] + pub cron: Option, /// Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`. #[serde(skip_serializing_if = "Option::is_none")] pub display_prompt: Option, /// Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). pub id: i64, - /// Interval between scheduled ticks, in milliseconds. - pub interval_ms: i64, + /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). + #[serde(skip_serializing_if = "Option::is_none")] + pub interval_ms: Option, /// ISO 8601 timestamp when the next tick is scheduled to fire. pub next_run_at: String, /// Prompt text that gets enqueued on every tick. pub prompt: String, /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). pub recurring: bool, + /// IANA timezone the `cron` expression is evaluated in. + #[serde(skip_serializing_if = "Option::is_none")] + pub tz: Option, } /// Snapshot of the currently active recurring prompts for this session. @@ -8087,6 +8142,36 @@ pub struct SendResult { pub message_id: String, } +/// Agents discovered across user, project, plugin, and remote sources. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ServerAgentList { + /// All discovered agents across all sources + pub agents: Vec, +} + +/// Instruction sources discovered across user, repository, and plugin sources. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ServerInstructionSourceList { + /// All discovered instruction sources + pub sources: Vec, +} + /// Schema for the `ServerSkill` type. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -10596,11 +10681,14 @@ pub struct TaskAgentInfo { /// Most recent response text from the agent #[serde(skip_serializing_if = "Option::is_none")] pub latest_response: Option, - /// Model used for the task when specified + /// Requested model override for the task when specified #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, /// Most recent prompt delivered to the agent. Updated whenever the agent receives a follow-up message. pub prompt: String, + /// Runtime model resolved for the task when available + #[serde(skip_serializing_if = "Option::is_none")] + pub resolved_model: Option, /// Result text from the task when available #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, @@ -12378,6 +12466,36 @@ pub struct SkillsDiscoverResult { pub skills: Vec, } +/// Agents discovered across user, project, plugin, and remote sources. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentsDiscoverResult { + /// All discovered agents across all sources + pub agents: Vec, +} + +/// Instruction sources discovered across user, repository, and plugin sources. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InstructionsDiscoverResult { + /// All discovered instruction sources + pub sources: Vec, +} + /// Result of opening a session. /// ///
@@ -13346,7 +13464,7 @@ pub struct SessionInstructionsGetSourcesParams { #[serde(rename_all = "camelCase")] pub struct SessionInstructionsGetSourcesResult { /// Instruction sources for the session - pub sources: Vec, + pub sources: Vec, } /// Indicates whether fleet mode was successfully activated. @@ -16519,7 +16637,7 @@ pub enum InstalledPluginSourceUrlSource { /// ///
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum InstructionsSourcesLocation { +pub enum InstructionSourceLocation { /// Instructions live in user-level configuration. #[serde(rename = "user")] User, @@ -16547,7 +16665,7 @@ pub enum InstructionsSourcesLocation { /// ///
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum InstructionsSourcesType { +pub enum InstructionSourceType { /// Instructions loaded from the user's home configuration. #[serde(rename = "home")] Home, diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index e42dde5fa..cd5132f48 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -35,6 +35,20 @@ impl<'a> ClientRpc<'a> { } } + /// `agents.*` sub-namespace. + pub fn agents(&self) -> ClientRpcAgents<'a> { + ClientRpcAgents { + client: self.client, + } + } + + /// `instructions.*` sub-namespace. + pub fn instructions(&self) -> ClientRpcInstructions<'a> { + ClientRpcInstructions { + client: self.client, + } + } + /// `mcp.*` sub-namespace. pub fn mcp(&self) -> ClientRpcMcp<'a> { ClientRpcMcp { @@ -232,6 +246,81 @@ impl<'a> ClientRpcAgentRegistry<'a> { } } +/// `agents.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcAgents<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcAgents<'a> { + /// Discovers custom agents across user, project, plugin, and remote sources. + /// + /// Wire method: `agents.discover`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include in agent discovery. + /// + /// # Returns + /// + /// Agents discovered across user, project, plugin, and remote sources. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn discover(&self, params: AgentsDiscoverRequest) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::AGENTS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `instructions.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcInstructions<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcInstructions<'a> { + /// Discovers instruction sources across user, repository, and plugin sources. + /// + /// Wire method: `instructions.discover`. + /// + /// # Parameters + /// + /// * `params` - Optional project paths to include in instruction discovery. + /// + /// # Returns + /// + /// Instruction sources discovered across user, repository, and plugin sources. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn discover( + &self, + params: InstructionsDiscoverRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::INSTRUCTIONS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `mcp.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcMcp<'a> { diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 7fb8fe0e0..e20d7d6ef 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -493,6 +493,9 @@ pub struct SessionResumeData { pub continue_pending_work: Option, /// Total number of persisted events in the session at the time of resume pub event_count: i64, + /// On-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd + #[serde(skip_serializing_if = "Option::is_none")] + pub events_file_size_bytes: Option, /// Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, @@ -572,18 +575,28 @@ pub struct SessionTitleChangedData { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionScheduleCreatedData { + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule + #[serde(skip_serializing_if = "Option::is_none")] + pub at: Option, + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz` + #[serde(skip_serializing_if = "Option::is_none")] + pub cron: Option, /// Optional user-facing label shown in the timeline instead of the actual prompt (e.g. `/skill-name args` when the prompt is a skill invocation expansion) #[serde(skip_serializing_if = "Option::is_none")] pub display_prompt: Option, /// Sequential id assigned to the scheduled prompt within the session pub id: i64, - /// Interval between ticks in milliseconds - pub interval_ms: i64, + /// Interval between ticks in milliseconds (relative-interval schedules) + #[serde(skip_serializing_if = "Option::is_none")] + pub interval_ms: Option, /// Prompt text that gets enqueued on every tick pub prompt: String, /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) #[serde(skip_serializing_if = "Option::is_none")] pub recurring: Option, + /// IANA timezone the `cron` expression is evaluated in + #[serde(skip_serializing_if = "Option::is_none")] + pub tz: Option, } /// Session event "session.schedule_cancelled". Scheduled prompt cancelled from the schedule manager dialog @@ -886,6 +899,9 @@ pub struct SessionShutdownData { /// Error description when shutdownType is "error" #[serde(skip_serializing_if = "Option::is_none")] pub error_reason: Option, + /// On-disk byte size of the session's persisted events.jsonl file at shutdown time; omitted when the file does not exist or cannot be stat'd + #[serde(skip_serializing_if = "Option::is_none")] + pub events_file_size_bytes: Option, /// Per-model usage breakdown, keyed by model identifier pub model_metrics: HashMap, /// Unix timestamp (milliseconds) when the session started @@ -2057,6 +2073,9 @@ pub struct HookEndData { pub struct HookProgressData { /// Human-readable progress message from the hook process pub message: String, + /// When true, this status message replaces the previous temporary one instead of accumulating + #[serde(skip_serializing_if = "Option::is_none")] + pub temporary: Option, } /// Metadata about the prompt template and its construction diff --git a/rust/tests/e2e/abort.rs b/rust/tests/e2e/abort.rs index 8d4ab5499..d4e79452b 100644 --- a/rust/tests/e2e/abort.rs +++ b/rust/tests/e2e/abort.rs @@ -47,11 +47,24 @@ async fn should_abort_during_active_streaming() { session.abort().await.expect("abort session"); - let recovery = session - .send_and_wait("Say 'abort_recovery_ok'.") + // Session should be usable after abort. Wait for the specific recovery + // message rather than racing against a late idle from the aborted turn. + let recovery_events = session.subscribe(); + session + .send("Say 'abort_recovery_ok'.") .await - .expect("send recovery") - .expect("assistant message"); + .expect("send recovery"); + let recovery = wait_for_event( + recovery_events, + "assistant.message containing abort_recovery_ok", + |event| { + event.parsed_type() == SessionEventType::AssistantMessage + && assistant_message_content(event) + .to_lowercase() + .contains("abort_recovery_ok") + }, + ) + .await; assert!( assistant_message_content(&recovery) .to_lowercase() diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 6e15842e3..e67890d98 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.60.tgz", - "integrity": "sha512-+GjW+GJNo55nwJwt48o9szWcyhuY0u682cBKQI1ay9jVBX8DCCXC6HB6Tyv5/MaM4N7CxTiEgp48aVMkye8K+g==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.61.tgz", + "integrity": "sha512-E4f7YXTL2uUZY/ypnfsUruAeSgrHx3AGYEbm5N0DrpzPqoNAZqV6kHEWM4vu+W/nGvydIfPxmOTqaMEhM8r0Uw==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.60", - "@github/copilot-darwin-x64": "1.0.60", - "@github/copilot-linux-arm64": "1.0.60", - "@github/copilot-linux-x64": "1.0.60", - "@github/copilot-linuxmusl-arm64": "1.0.60", - "@github/copilot-linuxmusl-x64": "1.0.60", - "@github/copilot-win32-arm64": "1.0.60", - "@github/copilot-win32-x64": "1.0.60" + "@github/copilot-darwin-arm64": "1.0.61", + "@github/copilot-darwin-x64": "1.0.61", + "@github/copilot-linux-arm64": "1.0.61", + "@github/copilot-linux-x64": "1.0.61", + "@github/copilot-linuxmusl-arm64": "1.0.61", + "@github/copilot-linuxmusl-x64": "1.0.61", + "@github/copilot-win32-arm64": "1.0.61", + "@github/copilot-win32-x64": "1.0.61" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.60.tgz", - "integrity": "sha512-TErNaVxsv+uB3bdHwdoKorCd1rhiRh7HkX48vnS7jwqa8EtGgAkzNrHKC7mruL2rnYOOsNIdPfhzQk+2Y6PSxQ==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.61.tgz", + "integrity": "sha512-10prvjHRXB0SD28NsIpzdNDgLquQYUwaH5Ev9KVdIWdBPAvlQsHmQ4JSCyD/UILc/nrrr02CKUgum+mZRKUKIg==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.60.tgz", - "integrity": "sha512-PthhcR6PqbQlT04xQKTElpPSJOrJd65nK/l9Sjmpwtk21RrDKs13DCY/19ubP17updYUWBxp3VNfyfN3DAQKOA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.61.tgz", + "integrity": "sha512-NXUjageJ3mxDfHtXGYu//XhJ+dhJFYObT4R3jeWgIHhd+4lX7FlC754nwlBP/ZuVhJ3ND22JK9sua9d2F3Cbwg==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.60.tgz", - "integrity": "sha512-AVahkDVQTiGmHvDjlb4CHO8CFEGqmCEipxi0qTA60oH3Y3W2C4aYBwEBtP/85pN3wUUKZJVrWTCcxdufUBuK2Q==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.61.tgz", + "integrity": "sha512-dwB2+QSMr622JkePeK56M7YWXsTT/DQzKfpDq8Lk2kmGU052RZAarRmt8gcNm4anofN7pMSrqc3YHj1TM84MFw==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.60.tgz", - "integrity": "sha512-NwQjV2ZyUdJVAO4t7wiT+eR3uNWYP57xaLUIhf6JTMGpsTyN+mAFXW63xpwM/K+Pug62uRDQDBjEeOQRB7qZrA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.61.tgz", + "integrity": "sha512-q6n8R8oybvuCmmkP+43w809Wpud/wwRi/fFSZEYJagiNGmYJ00SDkrfJxHbZsAFMpaJC+oTswqzJHjRoZbO74w==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.60.tgz", - "integrity": "sha512-AYGPc9vq2k248bVwUbiVJ65kIYYMQQ7ci+S3oefWBIyYtYwAH0n+Q/IGAj49IPrelBarYABAsX+EQZJJC8rhxw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.61.tgz", + "integrity": "sha512-yWo7JXnZS11eJpm68E1RWKMR47EwzPKj3V7GX0EMTd8Fw0T2Aurk9wt9p3c9w0v02nTO1DqJhi68KVWJPdVqvA==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.60.tgz", - "integrity": "sha512-9/F7yl0/9FpGvYR/TCQtbhu0vIaUVem6U7em85QYaEjkS45nK500pByCMWY0bXv2eSS8U2g+8FOAjfkyLlxwPw==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.61.tgz", + "integrity": "sha512-nHzx27Ac4B0fpD9CcmvyrGOBEMJ01CPRgVRP0yAl4wpU4cM2I6+9TPyfYThlWDqZqiUKGXC1ZRQ+B8cJREVGmA==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.60.tgz", - "integrity": "sha512-ZxxS+Ua1+7Puz80yTOpQ4WS+s32NjrxIsqo8gE0FpuZId16BGOGbWkzWQvR/k2AVBCqpLZ7SK3LfDVKuKJRbpA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.61.tgz", + "integrity": "sha512-k6knzI+K5HlZeJDS/yeJAfoYD4xcURWfuqunpTCyk1pDbIFxmrLSqR/TDi7KNlpsf883n5WqpnB06K5kysdHHQ==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.60", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.60.tgz", - "integrity": "sha512-e91ZlFz9J1lkadExLg36oN8Ms/xIa03vAEir3DmyCeYebZ+Y48vdS+BwhQEma+GLoxJUOhzHndCckGnMRfNIbA==", + "version": "1.0.61", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.61.tgz", + "integrity": "sha512-L6NZ6o73VZFHd7OoRaztV3Prh1PbW9HXqYsAx+XywNALQvE1u489WBUC1ggfYBW5MTBCf8mxSkYQdb3Am2omsw==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 99a7583d3..8b7016592 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.60", + "@github/copilot": "^1.0.61", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", diff --git a/test/snapshots/abort/should_abort_during_active_streaming.yaml b/test/snapshots/abort/should_abort_during_active_streaming.yaml index bd18eab2f..1f5c085eb 100644 --- a/test/snapshots/abort/should_abort_during_active_streaming.yaml +++ b/test/snapshots/abort/should_abort_during_active_streaming.yaml @@ -1,5 +1,16 @@ models: - claude-sonnet-4.5 +# Two stored conversations cover the two legal histories after aborting an +# in-flight streaming turn: +# conv0 (race-win): the essay assistant turn finalized before the abort, so +# it remains in history -> [system, user(essay), +# assistant(essay), user(recovery)]. +# conv1 (race-lose): the abort dropped the in-flight essay assistant turn, so +# it is absent from history -> [system, user(essay), +# user(recovery)]. This is the common outcome with the +# Rust-reqwest transport in CLI 1.0.61+. +# The replay proxy matches a request as a strict prefix of a stored conversation +# and returns the next assistant message, so both histories resolve correctly. conversations: - messages: - role: system @@ -28,3 +39,13 @@ conversations: content: Say 'abort_recovery_ok'. - role: assistant content: abort_recovery_ok + - messages: + - role: system + content: ${system} + - role: user + content: Write a very long essay about the history of computing, covering every decade from the 1940s to the 2020s in + great detail. + - role: user + content: Say 'abort_recovery_ok'. + - role: assistant + content: abort_recovery_ok