/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using GitHub.Copilot.Rpc; namespace GitHub.Copilot; /// /// Declarative metadata for a single canvas, sent over the wire on /// session.create / session.resume. /// [Experimental(Diagnostics.Experimental)] public sealed class CanvasDeclaration { /// Canvas identifier, unique within the declaring connection. [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; /// Human-readable name shown in host UI and canvas pickers. [JsonPropertyName("displayName")] public string DisplayName { get; set; } = string.Empty; /// Short, single-sentence description shown to the agent in canvas catalogs. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; /// JSON Schema for the input payload accepted by canvas.open. [JsonPropertyName("inputSchema")] public JsonElement? InputSchema { get; set; } /// Agent-callable actions this canvas exposes. [JsonPropertyName("actions")] public IList? Actions { get; set; } } /// /// Stable extension identity for session participants that provide canvases. /// [Experimental(Diagnostics.Experimental)] public sealed class ExtensionInfo { /// Extension namespace/source, e.g. "github-app". [JsonPropertyName("source")] public string Source { get; set; } = string.Empty; /// Stable provider name within the source namespace. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; } /// Structured exception returned from canvas handlers. /// /// Throw this from implementations to surface a /// machine-readable error code to the runtime. Any other exception is wrapped /// in a generic canvas_handler_error envelope. /// [Experimental(Diagnostics.Experimental)] public sealed class CanvasException : Exception { /// Initializes a new . /// Machine-readable error code. /// Human-readable message. public CanvasException(string code, string message) : base(message) { Code = code; } /// Machine-readable error code. public string Code { get; } /// /// Default exception returned when a custom action has no handler. /// public static CanvasException NoHandler() => new( "canvas_action_no_handler", "No handler implemented for this canvas action"); } /// /// Internal helpers used by the session runtime to translate /// (and other handler-thrown exceptions) into structured JSON-RPC error responses. /// internal static class CanvasErrorHelpers { private const int InternalError = -32603; public static LocalRpcInvocationException HandlerUnset() => Build( "canvas_handler_unset", "No canvas handler is registered on this session"); public static LocalRpcInvocationException HandlerError(string message) => Build( "canvas_handler_error", message); public static LocalRpcInvocationException ToRpcException(CanvasException error) => Build(error.Code, error.Message); private static LocalRpcInvocationException Build(string code, string message) { JsonElement payload = JsonSerializer.SerializeToElement( new JsonObject { ["code"] = code, ["message"] = message }, TypesJsonContext.Default.JsonObject); return new LocalRpcInvocationException(InternalError, message, payload); } } /// /// Provider-side canvas lifecycle handler. /// /// /// A session installs a single via /// SessionConfigBase.CanvasHandler. The handler receives every /// inbound canvas.open / canvas.close / canvas.action.invoke /// JSON-RPC request the runtime issues for this session and decides — typically /// by inspecting — which /// application-side canvas should handle the call. /// /// The SDK does not maintain a per-canvas registry; multiplexing across /// declared canvases is the implementor's responsibility. /// /// /// Implementations targeting netstandard2.0 cannot rely on default /// interface methods; derive from to inherit /// sensible defaults for and . /// /// [Experimental(Diagnostics.Experimental)] public interface ICanvasHandler { /// Open a new canvas instance. Task OnOpenAsync(CanvasProviderOpenRequest context, CancellationToken cancellationToken); /// Canvas was closed by the user or agent. Default: no-op. Task OnCloseAsync(CanvasProviderCloseRequest context, CancellationToken cancellationToken); /// /// Handle a non-lifecycle action declared by the canvas. /// Default: throws . /// Task OnActionAsync(CanvasProviderInvokeActionRequest context, CancellationToken cancellationToken); } /// /// Convenience base class for that supplies /// default no-op / no-handler implementations of the optional callbacks. /// [Experimental(Diagnostics.Experimental)] public abstract class CanvasHandlerBase : ICanvasHandler { /// public abstract Task OnOpenAsync(CanvasProviderOpenRequest context, CancellationToken cancellationToken); /// public virtual Task OnCloseAsync(CanvasProviderCloseRequest context, CancellationToken cancellationToken) #if NET8_0_OR_GREATER => Task.CompletedTask; #else => Task.FromResult(null); #endif /// public virtual Task OnActionAsync(CanvasProviderInvokeActionRequest context, CancellationToken cancellationToken) => Task.FromException(CanvasException.NoHandler()); }