@github/copilot-sdk 0.1.33-unstable.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,15 +26,16 @@ npm start
26
26
  ## Quick Start
27
27
 
28
28
  ```typescript
29
- import { CopilotClient } from "@github/copilot-sdk";
29
+ import { CopilotClient, approveAll } from "@github/copilot-sdk";
30
30
 
31
31
  // Create and start client
32
32
  const client = new CopilotClient();
33
33
  await client.start();
34
34
 
35
- // Create a session
35
+ // Create a session (onPermissionRequest is required)
36
36
  const session = await client.createSession({
37
37
  model: "gpt-5",
38
+ onPermissionRequest: approveAll,
38
39
  });
39
40
 
40
41
  // Wait for response using typed event handlers
@@ -59,7 +60,7 @@ await client.stop();
59
60
  Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+/Node.js 18.0+):
60
61
 
61
62
  ```typescript
62
- await using session = await client.createSession({ model: "gpt-5" });
63
+ await using session = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll });
63
64
  // session is automatically disconnected when leaving scope
64
65
  ```
65
66
 
@@ -82,9 +83,10 @@ new CopilotClient(options?: CopilotClientOptions)
82
83
  - `useStdio?: boolean` - Use stdio transport instead of TCP (default: true)
83
84
  - `logLevel?: string` - Log level (default: "info")
84
85
  - `autoStart?: boolean` - Auto-start server (default: true)
85
- - `autoRestart?: boolean` - Auto-restart on crash (default: true)
86
86
  - `githubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
87
87
  - `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `githubToken` is provided). Cannot be used with `cliUrl`.
88
+ - `telemetry?: TelemetryConfig` - OpenTelemetry configuration for the CLI process. Providing this object enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
89
+ - `onGetTraceContext?: TraceContextProvider` - Advanced: callback for linking your application's own OpenTelemetry spans into the same distributed trace as the CLI's spans. Not needed for normal telemetry collection. See [Telemetry](#telemetry) below.
88
90
 
89
91
  #### Methods
90
92
 
@@ -113,6 +115,7 @@ Create a new conversation session.
113
115
  - `systemMessage?: SystemMessageConfig` - System message customization (see below)
114
116
  - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below)
115
117
  - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section.
118
+ - `onPermissionRequest: PermissionHandler` - **Required.** Handler called before each tool execution to approve or deny it. Use `approveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section.
116
119
  - `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section.
117
120
  - `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
118
121
 
@@ -297,9 +300,10 @@ See `SessionEvent` type in the source for full details.
297
300
 
298
301
  ## Image Support
299
302
 
300
- The SDK supports image attachments via the `attachments` parameter. You can attach images by providing their file path:
303
+ The SDK supports image attachments via the `attachments` parameter. You can attach images by providing their file path, or by passing base64-encoded data directly using a blob attachment:
301
304
 
302
305
  ```typescript
306
+ // File attachment — runtime reads from disk
303
307
  await session.send({
304
308
  prompt: "What's in this image?",
305
309
  attachments: [
@@ -309,6 +313,18 @@ await session.send({
309
313
  },
310
314
  ],
311
315
  });
316
+
317
+ // Blob attachment — provide base64 data directly
318
+ await session.send({
319
+ prompt: "What's in this image?",
320
+ attachments: [
321
+ {
322
+ type: "blob",
323
+ data: base64ImageData,
324
+ mimeType: "image/png",
325
+ },
326
+ ],
327
+ });
312
328
  ```
313
329
 
314
330
  Supported image formats include JPG, PNG, GIF, and other common image types. The agent's `view` tool can also read images directly from the filesystem, so you can also ask questions like:
@@ -426,6 +442,19 @@ defineTool("edit_file", {
426
442
  })
427
443
  ```
428
444
 
445
+ #### Skipping Permission Prompts
446
+
447
+ Set `skipPermission: true` on a tool definition to allow it to execute without triggering a permission prompt:
448
+
449
+ ```ts
450
+ defineTool("safe_lookup", {
451
+ description: "A read-only lookup that needs no confirmation",
452
+ parameters: z.object({ id: z.string() }),
453
+ skipPermission: true,
454
+ handler: async ({ id }) => { /* your logic */ },
455
+ })
456
+ ```
457
+
429
458
  ### System Message Customization
430
459
 
431
460
  Control the system prompt using `systemMessage` in session config:
@@ -444,7 +473,45 @@ const session = await client.createSession({
444
473
  });
445
474
  ```
446
475
 
447
- The SDK auto-injects environment context, tool instructions, and security guardrails. The default CLI persona is preserved, and your `content` is appended after SDK-managed sections. To change the persona or fully redefine the prompt, use `mode: "replace"`.
476
+ The SDK auto-injects environment context, tool instructions, and security guardrails. The default CLI persona is preserved, and your `content` is appended after SDK-managed sections. To change the persona or fully redefine the prompt, use `mode: "replace"` or `mode: "customize"`.
477
+
478
+ #### Customize Mode
479
+
480
+ Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
481
+
482
+ ```typescript
483
+ import { SYSTEM_PROMPT_SECTIONS } from "@github/copilot-sdk";
484
+ import type { SectionOverride, SystemPromptSection } from "@github/copilot-sdk";
485
+
486
+ const session = await client.createSession({
487
+ model: "gpt-5",
488
+ systemMessage: {
489
+ mode: "customize",
490
+ sections: {
491
+ // Replace the tone/style section
492
+ tone: { action: "replace", content: "Respond in a warm, professional tone. Be thorough in explanations." },
493
+ // Remove coding-specific rules
494
+ code_change_rules: { action: "remove" },
495
+ // Append to existing guidelines
496
+ guidelines: { action: "append", content: "\n* Always cite data sources" },
497
+ },
498
+ // Additional instructions appended after all sections
499
+ content: "Focus on financial analysis and reporting.",
500
+ },
501
+ });
502
+ ```
503
+
504
+ Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `last_instructions`. Use the `SYSTEM_PROMPT_SECTIONS` constant for descriptions of each section.
505
+
506
+ Each section override supports four actions:
507
+ - **`replace`** — Replace the section content entirely
508
+ - **`remove`** — Remove the section from the prompt
509
+ - **`append`** — Add content after the existing section
510
+ - **`prepend`** — Add content before the existing section
511
+
512
+ Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
513
+
514
+ #### Replace Mode
448
515
 
449
516
  For full control (removes all guardrails), use `mode: "replace"`:
450
517
 
@@ -589,6 +656,127 @@ const session = await client.createSession({
589
656
  > - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`.
590
657
  > - The `baseUrl` should be just the host (e.g., `https://my-resource.openai.azure.com`). Do **not** include `/openai/v1` in the URL - the SDK handles path construction automatically.
591
658
 
659
+ ## Telemetry
660
+
661
+ The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` config to enable trace export from the CLI process — this is all most users need:
662
+
663
+ ```typescript
664
+ const client = new CopilotClient({
665
+ telemetry: {
666
+ otlpEndpoint: "http://localhost:4318",
667
+ },
668
+ });
669
+ ```
670
+
671
+ With just this configuration, the CLI emits spans for every session, message, and tool call to your collector. No additional dependencies or setup required.
672
+
673
+ **TelemetryConfig options:**
674
+
675
+ - `otlpEndpoint?: string` - OTLP HTTP endpoint URL
676
+ - `filePath?: string` - File path for JSON-lines trace output
677
+ - `exporterType?: string` - `"otlp-http"` or `"file"`
678
+ - `sourceName?: string` - Instrumentation scope name
679
+ - `captureContent?: boolean` - Whether to capture message content
680
+
681
+ ### Advanced: Trace Context Propagation
682
+
683
+ > **You don't need this for normal telemetry collection.** The `telemetry` config above is sufficient to get full traces from the CLI.
684
+
685
+ `onGetTraceContext` is only needed if your application creates its own OpenTelemetry spans and you want them to appear in the **same distributed trace** as the CLI's spans — for example, to nest a "handle tool call" span inside the CLI's "execute tool" span, or to show the SDK call as a child of your application's request-handling span.
686
+
687
+ If you're already using `@opentelemetry/api` in your app and want this linkage, provide a callback:
688
+
689
+ ```typescript
690
+ import { propagation, context } from "@opentelemetry/api";
691
+
692
+ const client = new CopilotClient({
693
+ telemetry: { otlpEndpoint: "http://localhost:4318" },
694
+ onGetTraceContext: () => {
695
+ const carrier: Record<string, string> = {};
696
+ propagation.inject(context.active(), carrier);
697
+ return carrier;
698
+ },
699
+ });
700
+ ```
701
+
702
+ Inbound trace context from the CLI is available on the `ToolInvocation` object passed to tool handlers as `traceparent` and `tracestate` fields. See the [OpenTelemetry guide](../docs/observability/opentelemetry.md) for a full wire-up example.
703
+
704
+ ## Permission Handling
705
+
706
+ An `onPermissionRequest` handler is **required** whenever you create or resume a session. The handler is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and must return a decision.
707
+
708
+ ### Approve All (simplest)
709
+
710
+ Use the built-in `approveAll` helper to allow every tool call without any checks:
711
+
712
+ ```typescript
713
+ import { CopilotClient, approveAll } from "@github/copilot-sdk";
714
+
715
+ const session = await client.createSession({
716
+ model: "gpt-5",
717
+ onPermissionRequest: approveAll,
718
+ });
719
+ ```
720
+
721
+ ### Custom Permission Handler
722
+
723
+ Provide your own function to inspect each request and apply custom logic:
724
+
725
+ ```typescript
726
+ import type { PermissionRequest, PermissionRequestResult } from "@github/copilot-sdk";
727
+
728
+ const session = await client.createSession({
729
+ model: "gpt-5",
730
+ onPermissionRequest: (request: PermissionRequest, invocation): PermissionRequestResult => {
731
+ // request.kind — what type of operation is being requested:
732
+ // "shell" — executing a shell command
733
+ // "write" — writing or editing a file
734
+ // "read" — reading a file
735
+ // "mcp" — calling an MCP tool
736
+ // "custom-tool" — calling one of your registered tools
737
+ // "url" — fetching a URL
738
+ // "memory" — storing or retrieving persistent session memory
739
+ // "hook" — invoking a server-side hook or integration
740
+ // (additional kinds may be added; include a default case in handlers)
741
+ // request.toolCallId — the tool call that triggered this request
742
+ // request.toolName — name of the tool (for custom-tool / mcp)
743
+ // request.fileName — file being written (for write)
744
+ // request.fullCommandText — full shell command (for shell)
745
+
746
+ if (request.kind === "shell") {
747
+ // Deny shell commands
748
+ return { kind: "denied-interactively-by-user" };
749
+ }
750
+
751
+ return { kind: "approved" };
752
+ },
753
+ });
754
+ ```
755
+
756
+ ### Permission Result Kinds
757
+
758
+ | Kind | Meaning |
759
+ |------|---------|
760
+ | `"approved"` | Allow the tool to run |
761
+ | `"denied-interactively-by-user"` | User explicitly denied the request |
762
+ | `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked |
763
+ | `"denied-by-rules"` | Denied by a policy rule |
764
+ | `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy |
765
+ | `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) |
766
+ ### Resuming Sessions
767
+
768
+ Pass `onPermissionRequest` when resuming a session too — it is required:
769
+
770
+ ```typescript
771
+ const session = await client.resumeSession("session-id", {
772
+ onPermissionRequest: approveAll,
773
+ });
774
+ ```
775
+
776
+ ### Per-Tool Skip Permission
777
+
778
+ To let a specific custom tool bypass the permission prompt entirely, set `skipPermission: true` on the tool definition. See [Skipping Permission Prompts](#skipping-permission-prompts) under Tools.
779
+
592
780
  ## User Input Requests
593
781
 
594
782
  Enable the agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler: