Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6b62308
Phase A: property/method renames on SessionConfig/ResumeSessionConfig
SteveSandersonMS May 21, 2026
ff6fed5
Phase C: CopilotClientOptions / MCP / streaming shape changes
SteveSandersonMS May 21, 2026
325c9d0
Phase D: lifecycle event polymorphic union + Date timestamps
SteveSandersonMS May 21, 2026
328afa2
Phase E: hook input timestamps as Date
SteveSandersonMS May 21, 2026
f9f0083
Phase F: PermissionRequestResult.feedback + use generated PermissionR…
SteveSandersonMS May 21, 2026
dea6d55
Phase G: extract SessionConfigBase
SteveSandersonMS May 21, 2026
49da911
Phase H: defineTool({ name, ... }) single-arg form
SteveSandersonMS May 21, 2026
355a0f1
Revert "Phase H: defineTool({ name, ... }) single-arg form"
SteveSandersonMS May 21, 2026
b3c36e8
Phase I: RuntimeConnection discriminated config
SteveSandersonMS May 21, 2026
9ccf6d0
Phase J: send / sendAndWait string overloads
SteveSandersonMS May 21, 2026
9ba1bf1
Phase K: stripInternal, AsyncDisposable, clean stop(), drop destroy()
SteveSandersonMS May 21, 2026
659936e
Phase L: fix githubToken typos in scenario fixtures
SteveSandersonMS May 21, 2026
09dada7
Phase L follow-up: run prettier --write over all modified files
SteveSandersonMS May 21, 2026
6536140
Fix Phase I/E/L test failures surfaced by CI
SteveSandersonMS May 21, 2026
3ac7ae8
Add E2E equivalents of removed createSession/resumeSession-without-pe…
SteveSandersonMS May 21, 2026
14d0a9f
Add E2E tests for createSession/resumeSession without onPermissionReq…
SteveSandersonMS May 21, 2026
d1ac2ba
Harness: preserve caller-supplied RuntimeConnection while still injec…
SteveSandersonMS May 21, 2026
11df3b1
session_fs.e2e: fix unconverted tcpConnectionToken flat key on inner …
SteveSandersonMS May 21, 2026
a8a0e0c
Move hook input deserialization next to the cast that types it
SteveSandersonMS May 21, 2026
ae0a228
Phase K refinement: use unref() instead of destroy() in client.stop()
SteveSandersonMS May 21, 2026
9f4eece
client.stop(): await child exit and socket close
SteveSandersonMS May 21, 2026
8f3dda0
Revert incorrect feedback widening of PermissionRequestResult
SteveSandersonMS May 21, 2026
b1b2a35
Rename client.on -> client.onLifecycle for cross-SDK consistency
SteveSandersonMS May 21, 2026
bc0d2aa
Reformat src/types.ts after PermissionRequestResult revert
SteveSandersonMS May 21, 2026
10f0706
Address PR review comments from #1357
SteveSandersonMS May 21, 2026
a48a63d
logLevel: don't impose any default, match C#/Go
SteveSandersonMS May 21, 2026
eae19b1
Rename CopilotClientOptions.remote -> enableRemoteSessions for cross-…
SteveSandersonMS May 21, 2026
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
4 changes: 2 additions & 2 deletions docs/troubleshooting/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b
| Queueing (enqueue mode) | `send({ mode: "enqueue" })` | Buffer for sequential processing (default) |
| File attachments | `send({ attachments: [{ type: "file", path }] })` | Images auto-encoded and resized |
| Directory attachments | `send({ attachments: [{ type: "directory", path }] })` | Attach directory context |
| Get history | `getMessages()` | All session events |
| Get history | `getEvents()` | All session events |
| Abort | `abort()` | Cancel in-flight request |
| **Tools** | | |
| Register custom tools | `registerTools()` | Full JSON Schema support |
Expand Down Expand Up @@ -178,7 +178,7 @@ The `--share` option is not available via SDK. Workarounds:
const events: SessionEvent[] = [];
session.on((event) => events.push(event));
// ... after conversation ...
const messages = await session.getMessages();
const messages = await session.getEvents();
// Format as markdown yourself
```

Expand Down
45 changes: 22 additions & 23 deletions nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,17 @@ new CopilotClient(options?: CopilotClientOptions)

**Options:**

- `cliPath?: string` - Path to CLI executable (default: uses COPILOT_CLI_PATH env var or bundled instance)
- `cliArgs?: string[]` - Extra arguments prepended before SDK-managed flags (e.g. `["./dist-cli/index.js"]` when using `node`)
- `cliUrl?: string` - URL of existing CLI server to connect to (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). When provided, the client will not spawn a CLI process.
- `port?: number` - Server port (default: 0 for random)
- `useStdio?: boolean` - Use stdio transport instead of TCP (default: true)
- `logLevel?: string` - Log level (default: "info")
- `autoStart?: boolean` - Auto-start server (default: true)
- `connection?: RuntimeConnection` - How to connect to the Copilot runtime. Construct via the factory functions on `RuntimeConnection`:
- `RuntimeConnection.forStdio({ path?, args? })` (default) — spawn the runtime and communicate over its stdin/stdout.
- `RuntimeConnection.forTcp({ port?, connectionToken?, path?, args? })` — spawn the runtime as a TCP server.
- `RuntimeConnection.forUri(url, { connectionToken? })` — connect to an already-running runtime (mutually exclusive with `gitHubToken`/`useLoggedInUser`).
- `cwd?: string` - Working directory for the runtime process (default: current process cwd).
- `baseDirectory?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When not set, the runtime defaults to `~/.copilot`. Ignored when connecting via `RuntimeConnection.forUri`.
- `logLevel?: string` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
- `gitHubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
- `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `gitHubToken` is provided). Cannot be used with `cliUrl`.
- `copilotHome?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned CLI process. When not set, the CLI defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when using `cliUrl`.
- `telemetry?: TelemetryConfig` - OpenTelemetry configuration for the CLI process. Providing this object enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
- `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.
- `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `gitHubToken` is provided). Cannot be used with `RuntimeConnection.forUri`.
- `telemetry?: TelemetryConfig` - OpenTelemetry configuration for the runtime process. Providing this object enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
- `onGetTraceContext?: TraceContextProvider` - Advanced: callback for linking your application's own OpenTelemetry spans into the same distributed trace as the runtime's spans. Not needed for normal telemetry collection. See [Telemetry](#telemetry) below.

#### Methods

Expand Down Expand Up @@ -173,7 +172,7 @@ Request the TUI to switch to displaying the specified session. Only available in
Subscribe to a specific session lifecycle event type. Returns an unsubscribe function.

```typescript
const unsubscribe = client.on("session.foreground", (event) => {
const unsubscribe = client.onLifecycle("session.foreground", (event) => {
console.log(`Session ${event.sessionId} is now in foreground`);
});
```
Expand All @@ -183,7 +182,7 @@ const unsubscribe = client.on("session.foreground", (event) => {
Subscribe to all session lifecycle events. Returns an unsubscribe function.

```typescript
const unsubscribe = client.on((event) => {
const unsubscribe = client.onLifecycle((event) => {
console.log(`${event.type}: ${event.sessionId}`);
});
```
Expand Down Expand Up @@ -277,7 +276,7 @@ unsubscribe();

Abort the currently processing message in this session.

##### `getMessages(): Promise<SessionEvent[]>`
##### `getEvents(): Promise<SessionEvent[]>`

Get all events/messages from this session.

Expand Down Expand Up @@ -415,7 +414,7 @@ Note: `assistant.message` and `assistant.reasoning` (final events) are always se
### Manual Server Control

```typescript
const client = new CopilotClient({ autoStart: false });
const client = new CopilotClient({});

// Start manually
await client.start();
Expand Down Expand Up @@ -856,15 +855,15 @@ const session = await client.createSession({

The handler must return one of the `PermissionDecision` shapes (or `{ kind: "no-result" }`). Approval scopes are present-tense — they describe the decision to apply, not the outcome reported back on session events:

| Kind | Meaning | Extra fields |
| ------------------------ | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `"approve-once"` | Allow this single request | — |
| `"approve-for-session"` | Allow this request and remember the approval for the rest of the session | `approval?` (rule to remember), `domain?` (for URL approvals) |
| Kind | Meaning | Extra fields |
| ------------------------ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `"approve-once"` | Allow this single request | — |
| `"approve-for-session"` | Allow this request and remember the approval for the rest of the session | `approval?` (rule to remember), `domain?` (for URL approvals) |
| `"approve-for-location"` | Allow this request and persist the approval for this project location (git root or cwd) | `approval` (rule to persist), `locationKey` (location to persist under) |
| `"approve-permanently"` | Allow this request and persist the approval across sessions (currently used for URL domains) | `domain` (URL domain to approve) |
| `"reject"` | Deny the request | `feedback?` (optional string surfaced to the agent) |
| `"user-not-available"` | Deny the request because no user is available to confirm it | — |
| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | — |
| `"reject"` | Deny the request | `feedback?` (optional string surfaced to the agent) |
| `"user-not-available"` | Deny the request because no user is available to confirm it | — |
| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | — |

### Resuming Sessions

Expand Down Expand Up @@ -1026,7 +1025,7 @@ try {
## Requirements

- Node.js >= 18.0.0
- GitHub Copilot CLI installed and in PATH (or provide custom `cliPath`)
- GitHub Copilot CLI installed and in PATH (or provide a custom `connection`)

## License

Expand Down
20 changes: 8 additions & 12 deletions nodejs/examples/basic-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*--------------------------------------------------------------------------------------------*/

import { z } from "zod";
import { CopilotClient, defineTool } from "../src/index.js";
import { approveAll, CopilotClient, defineTool } from "@github/copilot-sdk";

console.log("🚀 Starting Copilot SDK Example\n");

Expand All @@ -20,27 +20,23 @@ const lookupFactTool = defineTool("lookup_fact", {
handler: ({ topic }) => facts[topic.toLowerCase()] ?? `No fact stored for ${topic}.`,
});

// Create client - will auto-start CLI server (searches PATH for "copilot")
const client = new CopilotClient({ logLevel: "info" });
const session = await client.createSession({ tools: [lookupFactTool] });
await using client = new CopilotClient({ logLevel: "info" });
await using session = await client.createSession({
onPermissionRequest: approveAll,
tools: [lookupFactTool],
});
console.log(`✅ Session created: ${session.sessionId}\n`);

// Listen to events
session.on((event) => {
console.log(`📢 Event [${event.type}]:`, JSON.stringify(event.data, null, 2));
});

// Send a simple message
console.log("💬 Sending message...");
const result1 = await session.sendAndWait({ prompt: "Tell me 2+2" });
const result1 = await session.sendAndWait("Tell me 2+2");
console.log("📝 Response:", result1?.data.content);

// Send another message that uses the tool
console.log("💬 Sending follow-up message...");
const result2 = await session.sendAndWait({ prompt: "Use lookup_fact to tell me about 'node'" });
const result2 = await session.sendAndWait("Use lookup_fact to tell me about 'node'");
console.log("📝 Response:", result2?.data.content);

// Clean up
await session.disconnect();
await client.stop();
console.log("✅ Done!");
Loading
Loading