Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,119 @@ await using var session = await client.CreateSessionAsync(new()

---

## Telemetry & Observability

The Copilot SDK supports [OpenTelemetry](https://opentelemetry.io/) for distributed tracing. Provide a `telemetry` configuration to the client to enable trace export from the CLI process and automatic [W3C Trace Context](https://www.w3.org/TR/trace-context/) propagation between the SDK and CLI.

### Enabling Telemetry

Pass a `telemetry` (or `Telemetry`) config when creating the client. This is the opt-in — no separate "enabled" flag is needed.

<details open>
<summary><strong>Node.js / TypeScript</strong></summary>

<!-- docs-validate: skip -->
```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient({
telemetry: {
otlpEndpoint: "http://localhost:4318",
},
});
```

Optional peer dependency: `@opentelemetry/api`

</details>

<details>
<summary><strong>Python</strong></summary>

<!-- docs-validate: skip -->
```python
from copilot import CopilotClient

client = CopilotClient(
telemetry={
"otlp_endpoint": "http://localhost:4318",
},
)
```

Install with telemetry extras: `pip install copilot-sdk[telemetry]` (provides `opentelemetry-api`)

</details>

<details>
<summary><strong>Go</strong></summary>

<!-- docs-validate: skip -->
```go
client, err := copilot.NewClient(copilot.ClientOptions{
Telemetry: &copilot.TelemetryConfig{
OTLPEndpoint: "http://localhost:4318",
},
})
```

Dependency: `go.opentelemetry.io/otel`

</details>

<details>
<summary><strong>.NET</strong></summary>

<!-- docs-validate: skip -->
```csharp
var client = new CopilotClient(new CopilotClientOptions
{
Telemetry = new TelemetryConfig
{
OtlpEndpoint = "http://localhost:4318",
},
});
```

No extra dependencies — uses built-in `System.Diagnostics.Activity`.

</details>

### TelemetryConfig Options

| Option | Node.js | Python | Go | .NET | Description |
|---|---|---|---|---|---|
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |

### File Export

To write traces to a local file instead of an OTLP endpoint:

<!-- docs-validate: skip -->
```typescript
const client = new CopilotClient({
telemetry: {
filePath: "./traces.jsonl",
exporterType: "file",
},
});
```

### Trace Context Propagation

Trace context is propagated automatically — no manual instrumentation is needed:

- **SDK → CLI**: `traceparent` and `tracestate` headers from the current span/activity are included in `session.create`, `session.resume`, and `session.send` RPC calls.
- **CLI → SDK**: When the CLI invokes tool handlers, the trace context from the CLI's span is propagated so your tool code runs under the correct parent span.

📖 **[OpenTelemetry Instrumentation Guide →](./observability/opentelemetry.md)** — detailed GenAI semantic conventions, event-to-attribute mapping, and complete examples.

---

## Learn More

- [Authentication Guide](./auth/index.md) - GitHub OAuth, environment variables, and BYOK
Expand All @@ -1406,6 +1519,7 @@ await using var session = await client.CreateSessionAsync(new()
- [Using MCP Servers](./features/mcp.md) - Integrate external tools via Model Context Protocol
- [GitHub MCP Server Documentation](https://github.com/github/github-mcp-server)
- [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Explore more MCP servers
- [OpenTelemetry Instrumentation](./observability/opentelemetry.md) - Add tracing to your SDK usage

---

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Detailed API reference for each session hook.

### [Observability](./observability/opentelemetry.md)

- [OpenTelemetry Instrumentation](./observability/opentelemetry.md)add tracing to your SDK usage
- [OpenTelemetry Instrumentation](./observability/opentelemetry.md)built-in TelemetryConfig, trace context propagation, and application-level tracing

### [Integrations](./integrations/microsoft-agent-framework.md)

Expand Down
98 changes: 97 additions & 1 deletion docs/observability/opentelemetry.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,102 @@
# OpenTelemetry Instrumentation for Copilot SDK

This guide shows how to add OpenTelemetry tracing to your Copilot SDK applications using GenAI semantic conventions.
This guide shows how to add OpenTelemetry tracing to your Copilot SDK applications.

## Built-in Telemetry Support

The SDK has built-in support for configuring OpenTelemetry on the CLI process and propagating W3C Trace Context between the SDK and CLI. Provide a `TelemetryConfig` when creating the client to opt in:

<details open>
<summary><strong>Node.js / TypeScript</strong></summary>

<!-- docs-validate: skip -->
```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient({
telemetry: {
otlpEndpoint: "http://localhost:4318",
},
});
```

</details>

<details>
<summary><strong>Python</strong></summary>

<!-- docs-validate: skip -->
```python
from copilot import CopilotClient

client = CopilotClient(
telemetry={
"otlp_endpoint": "http://localhost:4318",
},
)
```

</details>

<details>
<summary><strong>Go</strong></summary>

<!-- docs-validate: skip -->
```go
client, err := copilot.NewClient(copilot.ClientOptions{
Telemetry: &copilot.TelemetryConfig{
OTLPEndpoint: "http://localhost:4318",
},
})
```

</details>

<details>
<summary><strong>.NET</strong></summary>

<!-- docs-validate: skip -->
```csharp
var client = new CopilotClient(new CopilotClientOptions
{
Telemetry = new TelemetryConfig
{
OtlpEndpoint = "http://localhost:4318",
},
});
```

</details>

### TelemetryConfig Options

| Option | Node.js | Python | Go | .NET | Description |
|---|---|---|---|---|---|
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |

### Trace Context Propagation

Trace context is propagated automatically — no manual instrumentation is needed:

- **SDK → CLI**: `traceparent` and `tracestate` headers from the current span/activity are included in `session.create`, `session.resume`, and `session.send` RPC calls.
- **CLI → SDK**: When the CLI invokes tool handlers, the trace context from the CLI's span is propagated so your tool code runs under the correct parent span.

### Per-Language Dependencies

| Language | Dependency | Notes |
|---|---|---|
| Node.js | `@opentelemetry/api` | Optional peer dependency |
| Python | `opentelemetry-api` | Install with `pip install copilot-sdk[telemetry]` |
| Go | `go.opentelemetry.io/otel` | Required dependency |
| .NET | — | Uses built-in `System.Diagnostics.Activity` |

## Application-Level Instrumentation

The rest of this guide shows how to add your own OpenTelemetry spans around SDK operations using GenAI semantic conventions. This is complementary to the built-in `TelemetryConfig` above — you can use both together.

## Overview

Expand Down
27 changes: 27 additions & 0 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ new CopilotClient(CopilotClientOptions? options = null)
- `Logger` - `ILogger` instance for SDK logging
- `GitHubToken` - GitHub token for authentication. When provided, takes priority over other auth methods.
- `UseLoggedInUser` - Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `CliUrl`.
- `Telemetry` - OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.

#### Methods

Expand Down Expand Up @@ -529,6 +530,32 @@ var session = await client.CreateSessionAsync(new SessionConfig
});
```

## Telemetry

The SDK supports OpenTelemetry for distributed tracing. Provide a `Telemetry` config to enable trace export and automatic W3C Trace Context propagation.

```csharp
var client = new CopilotClient(new CopilotClientOptions
{
Telemetry = new TelemetryConfig
{
OtlpEndpoint = "http://localhost:4318",
},
});
```

**TelemetryConfig properties:**

- `OtlpEndpoint` - OTLP HTTP endpoint URL
- `FilePath` - File path for JSON-lines trace output
- `ExporterType` - `"otlp-http"` or `"file"`
- `SourceName` - Instrumentation scope name
- `CaptureContent` - Whether to capture message content

Trace context (`traceparent`/`tracestate`) is automatically propagated between the SDK and CLI on `CreateSessionAsync`, `ResumeSessionAsync`, and `SendAsync` calls, and inbound when the CLI invokes tool handlers.

No extra dependencies — uses built-in `System.Diagnostics.Activity`.

## User Input Requests

Enable the agent to ask questions to the user using the `ask_user` tool by providing an `OnUserInputRequest` handler:
Expand Down
37 changes: 32 additions & 5 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance

try
{
var (traceparent, tracestate) = TelemetryHelpers.GetTraceContext();

var request = new CreateSessionRequest(
config.Model,
sessionId,
Expand All @@ -448,7 +450,9 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
config.ConfigDir,
config.SkillDirectories,
config.DisabledSkills,
config.InfiniteSessions);
config.InfiniteSessions,
traceparent,
tracestate);

var response = await InvokeRpcAsync<CreateSessionResponse>(
connection.Rpc, "session.create", [request], cancellationToken);
Expand Down Expand Up @@ -530,6 +534,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes

try
{
var (traceparent, tracestate) = TelemetryHelpers.GetTraceContext();

var request = new ResumeSessionRequest(
sessionId,
config.ClientName,
Expand All @@ -553,7 +559,9 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
config.Agent,
config.SkillDirectories,
config.DisabledSkills,
config.InfiniteSessions);
config.InfiniteSessions,
traceparent,
tracestate);

var response = await InvokeRpcAsync<ResumeSessionResponse>(
connection.Rpc, "session.resume", [request], cancellationToken);
Expand Down Expand Up @@ -1064,6 +1072,17 @@ private async Task VerifyProtocolVersionAsync(Connection connection, Cancellatio
startInfo.Environment["COPILOT_SDK_AUTH_TOKEN"] = options.GitHubToken;
}

// Set telemetry environment variables if configured
if (options.Telemetry is { } telemetry)
{
startInfo.Environment["COPILOT_OTEL_ENABLED"] = "true";
if (telemetry.OtlpEndpoint is not null) startInfo.Environment["OTEL_EXPORTER_OTLP_ENDPOINT"] = telemetry.OtlpEndpoint;
if (telemetry.FilePath is not null) startInfo.Environment["COPILOT_OTEL_FILE_EXPORTER_PATH"] = telemetry.FilePath;
if (telemetry.ExporterType is not null) startInfo.Environment["COPILOT_OTEL_EXPORTER_TYPE"] = telemetry.ExporterType;
if (telemetry.SourceName is not null) startInfo.Environment["COPILOT_OTEL_SOURCE_NAME"] = telemetry.SourceName;
if (telemetry.CaptureContent is { } capture) startInfo.Environment["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = capture ? "true" : "false";
}

var cliProcess = new Process { StartInfo = startInfo };
cliProcess.Start();

Expand Down Expand Up @@ -1320,8 +1339,12 @@ public async Task<HooksInvokeResponse> OnHooksInvoke(string sessionId, string ho
public async Task<ToolCallResponseV2> OnToolCallV2(string sessionId,
string toolCallId,
string toolName,
object? arguments)
object? arguments,
string? traceparent = null,
string? tracestate = null)
{
using var _ = TelemetryHelpers.RestoreTraceContext(traceparent, tracestate);

var session = client.GetSession(sessionId) ?? throw new ArgumentException($"Unknown session {sessionId}");
if (session.GetTool(toolName) is not { } tool)
{
Expand Down Expand Up @@ -1453,7 +1476,9 @@ internal record CreateSessionRequest(
string? ConfigDir,
List<string>? SkillDirectories,
List<string>? DisabledSkills,
InfiniteSessionConfig? InfiniteSessions);
InfiniteSessionConfig? InfiniteSessions,
string? Traceparent = null,
string? Tracestate = null);

internal record ToolDefinition(
string Name,
Expand Down Expand Up @@ -1496,7 +1521,9 @@ internal record ResumeSessionRequest(
string? Agent,
List<string>? SkillDirectories,
List<string>? DisabledSkills,
InfiniteSessionConfig? InfiniteSessions);
InfiniteSessionConfig? InfiniteSessions,
string? Traceparent = null,
string? Tracestate = null);

internal record ResumeSessionResponse(
string SessionId,
Expand Down
Loading
Loading