Skip to content

Latest commit

 

History

History
431 lines (328 loc) · 23.5 KB

File metadata and controls

431 lines (328 loc) · 23.5 KB

Changelog

All notable changes to the Copilot SDK are documented in this file.

This changelog is automatically generated by an AI agent when stable releases are published. See GitHub Releases for the full list.

v0.2.1 (2026-04-03)

Feature: commands and UI elicitation across all four SDKs

Register slash commands that CLI users can invoke and drive interactive input dialogs from any SDK language. This feature was previously Node.js-only; it now ships in Python, Go, and .NET as well. (#906, #908, #960)

const session = await client.createSession({
  onPermissionRequest: approveAll,
  commands: [{
    name: "summarize",
    description: "Summarize the conversation",
    handler: async (context) => { /* ... */ },
  }],
  onElicitationRequest: async (context) => {
    if (context.type === "confirm") return { action: "confirm" };
  },
});

// Drive dialogs from the session
const confirmed = await session.ui.confirm({ message: "Proceed?" });
const choice = await session.ui.select({ message: "Pick one", options: ["A", "B"] });
var session = await client.CreateSessionAsync(new SessionConfig {
    OnPermissionRequest = PermissionHandler.ApproveAll,
    Commands = [
        new CommandDefinition {
            Name = "summarize",
            Description = "Summarize the conversation",
            Handler = async (context) => { /* ... */ },
        }
    ],
});

// Drive dialogs from the session
var confirmed = await session.Ui.ConfirmAsync(new ConfirmOptions { Message = "Proceed?" });

⚠️ Breaking change (Node.js): The onElicitationRequest handler signature changed from two arguments (request, invocation) to a single ElicitationContext that combines both. Update callers to use context.sessionId and context.message directly.

Feature: session.getMetadata across all SDKs

Efficiently fetch metadata for a single session by ID without listing all sessions. Returns undefined/null (not an error) when the session is not found. (#899)

  • TypeScript: const meta = await client.getSessionMetadata(sessionId);
  • C#: var meta = await client.GetSessionMetadataAsync(sessionId);
  • Python: meta = await client.get_session_metadata(session_id)
  • Go: meta, err := client.GetSessionMetadata(ctx, sessionID)

Feature: sessionFs for virtualizing per-session storage (Node SDK)

Supply a custom sessionFs adapter in Node SDK session config to redirect the runtime's per-session storage (event log, large output files) to any backing store — useful for serverless deployments or custom persistence layers. (#917)

Other changes

  • bugfix: structured tool results (with toolTelemetry, resultType, etc.) now sent via RPC as objects instead of being stringified, preserving metadata for Node, Go, and Python SDKs (#970)
  • feature: [Python] CopilotClient and CopilotSession now support async with for automatic resource cleanup (#475)
  • improvement: [Python] copilot.types module removed; import types directly from copilot (#871)
  • improvement: [Python] workspace_path now accepts any os.PathLike and session.workspace_path returns a pathlib.Path (#901)
  • improvement: [Go] simplified rpc package API: renamed structs drop the redundant Rpc infix (e.g. ModelRpcApiModelApi) (#905)
  • fix: [Go] Session.SetModel now takes a pointer for optional options instead of a variadic argument (#904)

New contributors

  • @Sumanth007 made their first contribution in #475
  • @jongalloway made their first contribution in #957
  • @Morabbin made their first contribution in #970
  • @schneidafunk made their first contribution in #998

v0.2.0 (2026-03-20)

This is a big update with a broad round of API refinements, new capabilities, and cross-SDK consistency improvements that have shipped incrementally through preview releases since v0.1.32.

Highlights

Fine-grained system prompt customization

A new "customize" mode for systemMessage lets you surgically edit individual sections of the Copilot system prompt — without replacing the entire thing. Ten sections are configurable: identity, tone, tool_efficiency, environment_context, code_change_rules, guidelines, safety, tool_instructions, custom_instructions, and last_instructions.

Each section supports four static actions (replace, remove, append, prepend) and a transform callback that receives the current rendered content and returns modified text — useful for regex mutations, conditional edits, or logging what the prompt contains. (#816)

const session = await client.createSession({
  onPermissionRequest: approveAll,
  systemMessage: {
    mode: "customize",
    sections: {
      identity: {
        action: (current) => current.replace("GitHub Copilot", "Acme Assistant"),
      },
      tone: { action: "replace", content: "Be concise and professional." },
      code_change_rules: { action: "remove" },
    },
  },
});
var session = await client.CreateSessionAsync(new SessionConfig {
    OnPermissionRequest = PermissionHandler.ApproveAll,
    SystemMessage = new SystemMessageConfig {
        Mode = SystemMessageMode.Customize,
        Sections = new Dictionary<string, SectionOverride> {
            ["identity"] = new() {
                Transform = current => Task.FromResult(current.Replace("GitHub Copilot", "Acme Assistant")),
            },
            ["tone"] = new() { Action = SectionOverrideAction.Replace, Content = "Be concise and professional." },
            ["code_change_rules"] = new() { Action = SectionOverrideAction.Remove },
        },
    },
});

OpenTelemetry support across all SDKs

All four SDK languages now support distributed tracing with the Copilot CLI. Set telemetry in your client options to configure an OTLP exporter; W3C trace context is automatically propagated on session.create, session.resume, and session.send, and restored in tool handlers so tool execution is linked to the originating trace. (#785)

const client = new CopilotClient({
  telemetry: {
    otlpEndpoint: "http://localhost:4318",
    sourceName: "my-app",
  },
});
var client = new CopilotClient(new CopilotClientOptions {
    Telemetry = new TelemetryConfig {
        OtlpEndpoint = "http://localhost:4318",
        SourceName = "my-app",
    },
});
  • Python: CopilotClient(SubprocessConfig(telemetry={"otlp_endpoint": "http://localhost:4318", "source_name": "my-app"}))
  • Go: copilot.NewClient(&copilot.ClientOptions{Telemetry: &copilot.TelemetryConfig{OTLPEndpoint: "http://localhost:4318", SourceName: "my-app"}})

Blob attachments for inline binary data

A new blob attachment type lets you send images or other binary content directly to a session without writing to disk — useful when data is already in memory (screenshots, API responses, generated images). (#731)

await session.send({
  prompt: "What's in this image?",
  attachments: [{ type: "blob", data: base64Str, mimeType: "image/png" }],
});
await session.SendAsync(new MessageOptions {
    Prompt = "What's in this image?",
    Attachments = [new UserMessageDataAttachmentsItemBlob { Data = base64Str, MimeType = "image/png" }],
});

Pre-select a custom agent at session creation

You can now specify which custom agent should be active when a session starts, eliminating the need for a separate session.rpc.agent.select() call. (#722)

const session = await client.createSession({
  customAgents: [
    { name: "researcher", prompt: "You are a research assistant." },
    { name: "editor", prompt: "You are a code editor." },
  ],
  agent: "researcher",
  onPermissionRequest: approveAll,
});
var session = await client.CreateSessionAsync(new SessionConfig {
    CustomAgents = [
        new CustomAgentConfig { Name = "researcher", Prompt = "You are a research assistant." },
        new CustomAgentConfig { Name = "editor", Prompt = "You are a code editor." },
    ],
    Agent = "researcher",
    OnPermissionRequest = PermissionHandler.ApproveAll,
});

New features

  • skipPermission on tool definitions — Tools can now be registered with skipPermission: true to bypass the confirmation prompt for low-risk operations like read-only queries. Available in all four SDKs. (#808)
  • reasoningEffort when switching models — All SDKs now accept an optional reasoningEffort parameter in setModel() for models that support it. (#712)
  • Custom model listing for BYOK — Applications using bring-your-own-key providers can supply onListModels in client options to override client.listModels() with their own model list. (#730)
  • no-result permission outcome — Permission handlers can now return "no-result" so extensions can attach to sessions without actively answering permission requests. (#802)
  • SessionConfig.onEvent catch-all — A new onEvent handler on session config is registered before the RPC is issued, guaranteeing that early events like session.start are never dropped. (#664)
  • Node.js CJS compatibility — The Node.js SDK now ships both ESM and CJS builds, fixing crashes in VS Code extensions and other tools bundled with esbuild's format: "cjs". No changes needed in consumer code. (#546)
  • Experimental API annotations — APIs marked experimental in the schema (agent, fleet, compaction groups) are now annotated in all four SDKs: [Experimental] in C#, /** @experimental */ in TypeScript, and comments in Python and Go. (#875)
  • System notifications and session log APIs — Updated to match the latest CLI runtime, adding system.notification events and a session log RPC API. (#737)

Improvements

  • [.NET, Go] Serialize event dispatch so handlers are invoked in registration order with no concurrent calls (#791)
  • [Go] Detach CLI process lifespan from the context passed to Client.Start so cancellation no longer kills the child process (#689)
  • [Go] Stop RPC client logging expected EOF errors (#609)
  • [.NET] Emit XML doc comments from schema descriptions in generated RPC code (#724)
  • [.NET] Use lazy property initialization in generated RPC classes (#725)
  • [.NET] Add DebuggerDisplay attribute to SessionEvent for easier debugging (#726)
  • [.NET] Optional RPC params are now represented as optional method params for forward-compatible generated code (#733)
  • [.NET] Replace Task.WhenAny + Task.Delay timeout pattern with .WaitAsync(TimeSpan) (#805)
  • [.NET] Add NuGet package icon (#688)
  • [Node] Don't resolve cliPath when cliUrl is already set (#787)

New RPC methods

We've added low-level RPC methods to control a lot more of what's going on in the session. These are emerging APIs that don't yet have friendly wrappers, and some may be flagged as experimental or subject to change.

  • session.rpc.skills.list(), .enable(name), .disable(name), .reload()
  • session.rpc.mcp.list(), .enable(name), .disable(name), .reload()
  • session.rpc.extensions.list(), .enable(name), .disable(name), .reload()
  • session.rpc.plugins.list()
  • session.rpc.ui.elicitation(...) — structured user input
  • session.rpc.shell.exec(command), .kill(pid)
  • session.log(message, level, ephemeral)

In an forthcoming update, we'll add friendlier wrappers for these.

Bug fixes

  • [.NET] Fix SessionEvent.ToJson() failing for events with JsonElement-backed payloads (assistant.message, tool.execution_start, etc.) (#868)
  • [.NET] Add fallback TypeInfoResolver for StreamJsonRpc.RequestId to fix NativeAOT compatibility (#783)
  • [.NET] Fix codegen for discriminated unions nested within other types (#736)
  • [.NET] Handle unknown session event types gracefully instead of throwing (#881)

⚠️ Breaking changes

All SDKs

  • autoRestart removed — The autoRestart option has been deprecated across all SDKs (it was never fully implemented). The property still exists but has no effect and will be removed in a future release. Remove any references to autoRestart from your client options. (#803)

Python

The Python SDK received a significant API surface overhaul in this release, replacing loosely-typed TypedDict config objects with proper keyword arguments and dataclasses. These changes improve IDE autocompletion, type safety, and readability.

  • CopilotClient constructor redesigned — The CopilotClientOptions TypedDict has been replaced by two typed config dataclasses. (#793)

    # Before (v0.1.x)
    client = CopilotClient({"cli_url": "localhost:3000"})
    client = CopilotClient({"cli_path": "/usr/bin/copilot", "log_level": "debug"})
    
    # After (v0.2.0)
    client = CopilotClient(ExternalServerConfig(url="localhost:3000"))
    client = CopilotClient(SubprocessConfig(cli_path="/usr/bin/copilot", log_level="debug"))
  • create_session() and resume_session() now take keyword arguments instead of a SessionConfig / ResumeSessionConfig TypedDict. on_permission_request is now a required keyword argument. (#587)

    # Before
    session = await client.create_session({
        "on_permission_request": PermissionHandler.approve_all,
        "model": "gpt-4.1",
    })
    
    # After
    session = await client.create_session(
        on_permission_request=PermissionHandler.approve_all,
        model="gpt-4.1",
    )
  • send() and send_and_wait() take a positional prompt string instead of a MessageOptions TypedDict. Attachments and mode are now keyword arguments. (#814)

    # Before
    await session.send({"prompt": "Hello!"})
    await session.send_and_wait({"prompt": "What is 2+2?"})
    
    # After
    await session.send("Hello!")
    await session.send_and_wait("What is 2+2?")
  • MessageOptions, SessionConfig, and ResumeSessionConfig removed from public API — These TypedDicts are no longer exported. Use the new keyword-argument signatures directly. (#587, #814)

  • Internal modules renamed to privatecopilot.jsonrpc, copilot.sdk_protocol_version, and copilot.telemetry are now copilot._jsonrpc, copilot._sdk_protocol_version, and copilot._telemetry. If you were importing from these modules directly, update your imports. (#884)

  • Typed overloads for CopilotClient.on() — Event registration now uses typed overloads for better autocomplete. This shouldn't break existing code but changes the type signature. (#589)

Go

  • Client.Start() context no longer kills the CLI process — Previously, canceling the context.Context passed to Start() would terminate the spawned CLI process (it used exec.CommandContext). Now the CLI process lifespan is independent of that context — call client.Stop() or client.ForceStop() to shut it down. (#689)

  • LogOptions.Ephemeral changed from bool to *bool — This enables proper three-state semantics (unset/true/false). Use copilot.Bool(true) instead of a bare true. (#827)

    // Before
    session.Log(ctx, copilot.LogOptions{Level: copilot.LevelInfo, Ephemeral: true}, "message")
    
    // After
    session.Log(ctx, copilot.LogOptions{Level: copilot.LevelInfo, Ephemeral: copilot.Bool(true)}, "message")

v0.1.32 (2026-03-07)

Feature: backward compatibility with v2 CLI servers

SDK applications written against the v3 API now also work when connected to a v2 CLI server, with no code changes required. The SDK detects the server's protocol version and automatically adapts v2 tool.call and permission.request messages into the same user-facing handlers used by v3. (#706)

const session = await client.createSession({
  tools: [myTool],           // unchanged — works with v2 and v3 servers
  onPermissionRequest: approveAll,
});
var session = await client.CreateSessionAsync(new SessionConfig {
    Tools = [myTool],          // unchanged — works with v2 and v3 servers
    OnPermissionRequest = approveAll,
});

v0.1.31 (2026-03-07)

Feature: multi-client tool and permission broadcasts (protocol v3)

The SDK now uses protocol version 3, where the runtime broadcasts external_tool.requested and permission.requested as session events to all connected clients. This enables multi-client architectures where different clients contribute different tools, or where multiple clients observe the same permission prompts — if one client approves, all clients see the result. Your existing tool and permission handler code is unchanged. (#686)

// Two clients each register different tools; the agent can use both
const session1 = await client1.createSession({
  tools: [defineTool("search", { handler: doSearch })],
  onPermissionRequest: approveAll,
});
const session2 = await client2.resumeSession(session1.id, {
  tools: [defineTool("analyze", { handler: doAnalyze })],
  onPermissionRequest: approveAll,
});
var session1 = await client1.CreateSessionAsync(new SessionConfig {
    Tools = [AIFunctionFactory.Create(DoSearch, "search")],
    OnPermissionRequest = PermissionHandlers.ApproveAll,
});
var session2 = await client2.ResumeSessionAsync(session1.Id, new ResumeSessionConfig {
    Tools = [AIFunctionFactory.Create(DoAnalyze, "analyze")],
    OnPermissionRequest = PermissionHandlers.ApproveAll,
});

Feature: strongly-typed PermissionRequestResultKind for .NET and Go

Rather than comparing result.Kind against undiscoverable magic strings like "approved" or "denied-interactively-by-user", .NET and Go now provide typed constants. Node and Python already had typed unions for this; this brings full parity. (#631)

session.OnPermissionCompleted += (e) => {
    if (e.Result.Kind == PermissionRequestResultKind.Approved) { /* ... */ }
    if (e.Result.Kind == PermissionRequestResultKind.DeniedInteractivelyByUser) { /* ... */ }
};
// Go: PermissionKindApproved, PermissionKindDeniedByRules,
//     PermissionKindDeniedCouldNotRequestFromUser, PermissionKindDeniedInteractivelyByUser
if result.Kind == copilot.PermissionKindApproved { /* ... */ }

Other changes

  • feature: [Python] [Go] add get_last_session_id() / GetLastSessionID() for SDK-wide parity (was already available in Node and .NET) (#671)
  • improvement: [Python] add timeout parameter to generated RPC methods, allowing callers to override the default 30s timeout for long-running operations (#681)
  • bugfix: [Go] PermissionRequest fields are now properly typed (ToolName, Diff, Path, etc.) instead of a generic Extra map[string]any catch-all (#685)

v0.1.30 (2026-03-03)

Feature: support overriding built-in tools

Applications can now override built-in tools such as grep, edit_file, or read_file. To do this, register a custom tool with the same name and set the override flag. Without the flag, the runtime will return an error if the name clashes with a built-in. (#636)

import { defineTool } from "@github/copilot-sdk";

const session = await client.createSession({
  tools: [defineTool("grep", {
    overridesBuiltInTool: true,
    handler: async (params) => `CUSTOM_GREP_RESULT: ${params.query}`,
  })],
  onPermissionRequest: approveAll,
});
var grep = AIFunctionFactory.Create(
    ([Description("Search query")] string query) => $"CUSTOM_GREP_RESULT: {query}",
    "grep",
    "Custom grep implementation",
    new AIFunctionFactoryOptions
    {
        AdditionalProperties = new ReadOnlyDictionary<string, object?>(
            new Dictionary<string, object?> { ["is_override"] = true })
    });

Feature: simpler API for changing model mid-session

While session.rpc.model.switchTo() already worked, there is now a convenience method directly on the session object. (#621)

  • TypeScript: await session.setModel("gpt-4.1")
  • C#: await session.SetModelAsync("gpt-4.1")
  • Python: await session.set_model("gpt-4.1")
  • Go: err := session.SetModel(ctx, "gpt-4.1")

Other changes

  • improvement: [C#] use event delegate for thread-safe, insertion-ordered event handler dispatch (#624)
  • improvement: [C#] deduplicate OnDisposeCall and improve implementation (#626)
  • improvement: [C#] remove unnecessary SemaphoreSlim locks for handler fields (#625)
  • bugfix: [Python] correct PermissionHandler.approve_all type annotations (#618)

New contributors

  • @giulio-leone made their first contribution in #618