diff --git a/docs/getting-started.md b/docs/getting-started.md
index 2c4e6159..fc28d8f3 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -240,7 +240,7 @@ Right now, you wait for the complete response before seeing anything. Let's make
Update `index.ts`:
```typescript
-import { CopilotClient, SessionEvent } from "@github/copilot-sdk";
+import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
@@ -249,13 +249,11 @@ const session = await client.createSession({
});
// Listen for response chunks
-session.on((event: SessionEvent) => {
- if (event.type === "assistant.message_delta") {
- process.stdout.write(event.data.deltaContent);
- }
- if (event.type === "session.idle") {
- console.log(); // New line when done
- }
+session.on("assistant.message_delta", (event) => {
+ process.stdout.write(event.data.deltaContent);
+});
+session.on("session.idle", () => {
+ console.log(); // New line when done
});
await session.sendAndWait({ prompt: "Tell me a short joke" });
@@ -401,7 +399,7 @@ Now for the powerful part. Let's give Copilot the ability to call your code by d
Update `index.ts`:
```typescript
-import { CopilotClient, defineTool, SessionEvent } from "@github/copilot-sdk";
+import { CopilotClient, defineTool } from "@github/copilot-sdk";
// Define a tool that Copilot can call
const getWeather = defineTool("get_weather", {
@@ -430,10 +428,8 @@ const session = await client.createSession({
tools: [getWeather],
});
-session.on((event: SessionEvent) => {
- if (event.type === "assistant.message_delta") {
- process.stdout.write(event.data.deltaContent);
- }
+session.on("assistant.message_delta", (event) => {
+ process.stdout.write(event.data.deltaContent);
});
await session.sendAndWait({
@@ -650,7 +646,7 @@ Let's put it all together into a useful interactive assistant:
Node.js / TypeScript
```typescript
-import { CopilotClient, defineTool, SessionEvent } from "@github/copilot-sdk";
+import { CopilotClient, defineTool } from "@github/copilot-sdk";
import * as readline from "readline";
const getWeather = defineTool("get_weather", {
@@ -677,10 +673,8 @@ const session = await client.createSession({
tools: [getWeather],
});
-session.on((event: SessionEvent) => {
- if (event.type === "assistant.message_delta") {
- process.stdout.write(event.data.deltaContent);
- }
+session.on("assistant.message_delta", (event) => {
+ process.stdout.write(event.data.deltaContent);
});
const rl = readline.createInterface({
diff --git a/nodejs/README.md b/nodejs/README.md
index b6e7aa47..e8951243 100644
--- a/nodejs/README.md
+++ b/nodejs/README.md
@@ -24,14 +24,13 @@ const session = await client.createSession({
model: "gpt-5",
});
-// Wait for response using session.idle event
+// Wait for response using typed event handlers
const done = new Promise((resolve) => {
- session.on((event) => {
- if (event.type === "assistant.message") {
- console.log(event.data.content);
- } else if (event.type === "session.idle") {
- resolve();
- }
+ session.on("assistant.message", (event) => {
+ console.log(event.data.content);
+ });
+ session.on("session.idle", () => {
+ resolve();
});
});
@@ -159,13 +158,34 @@ Send a message and wait until the session becomes idle.
Returns the final assistant message event, or undefined if none was received.
+##### `on(eventType: string, handler: TypedSessionEventHandler): () => void`
+
+Subscribe to a specific event type. The handler receives properly typed events.
+
+```typescript
+// Listen for specific event types with full type inference
+session.on("assistant.message", (event) => {
+ console.log(event.data.content); // TypeScript knows about event.data.content
+});
+
+session.on("session.idle", () => {
+ console.log("Session is idle");
+});
+
+// Listen to streaming events
+session.on("assistant.message_delta", (event) => {
+ process.stdout.write(event.data.deltaContent);
+});
+```
+
##### `on(handler: SessionEventHandler): () => void`
-Subscribe to session events. Returns an unsubscribe function.
+Subscribe to all session events. Returns an unsubscribe function.
```typescript
const unsubscribe = session.on((event) => {
- console.log(event);
+ // Handle any event type
+ console.log(event.type, event);
});
// Later...
@@ -231,27 +251,33 @@ const session = await client.createSession({
streaming: true,
});
-// Wait for completion using session.idle event
+// Wait for completion using typed event handlers
const done = new Promise((resolve) => {
- session.on((event) => {
- if (event.type === "assistant.message_delta") {
- // Streaming message chunk - print incrementally
- process.stdout.write(event.data.deltaContent);
- } else if (event.type === "assistant.reasoning_delta") {
- // Streaming reasoning chunk (if model supports reasoning)
- process.stdout.write(event.data.deltaContent);
- } else if (event.type === "assistant.message") {
- // Final message - complete content
- console.log("\n--- Final message ---");
- console.log(event.data.content);
- } else if (event.type === "assistant.reasoning") {
- // Final reasoning content (if model supports reasoning)
- console.log("--- Reasoning ---");
- console.log(event.data.content);
- } else if (event.type === "session.idle") {
- // Session finished processing
- resolve();
- }
+ session.on("assistant.message_delta", (event) => {
+ // Streaming message chunk - print incrementally
+ process.stdout.write(event.data.deltaContent);
+ });
+
+ session.on("assistant.reasoning_delta", (event) => {
+ // Streaming reasoning chunk (if model supports reasoning)
+ process.stdout.write(event.data.deltaContent);
+ });
+
+ session.on("assistant.message", (event) => {
+ // Final message - complete content
+ console.log("\n--- Final message ---");
+ console.log(event.data.content);
+ });
+
+ session.on("assistant.reasoning", (event) => {
+ // Final reasoning content (if model supports reasoning)
+ console.log("--- Reasoning ---");
+ console.log(event.data.content);
+ });
+
+ session.on("session.idle", () => {
+ // Session finished processing
+ resolve();
});
});
diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts
index 014a9b43..1a973d0f 100644
--- a/nodejs/src/index.ts
+++ b/nodejs/src/index.ts
@@ -33,6 +33,8 @@ export type {
SessionConfig,
SessionEvent,
SessionEventHandler,
+ SessionEventPayload,
+ SessionEventType,
SessionMetadata,
SystemMessageAppendConfig,
SystemMessageConfig,
@@ -41,5 +43,6 @@ export type {
ToolHandler,
ToolInvocation,
ToolResultObject,
+ TypedSessionEventHandler,
ZodSchema,
} from "./types.js";
diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts
index 22f745cf..ba6b42d2 100644
--- a/nodejs/src/session.ts
+++ b/nodejs/src/session.ts
@@ -15,9 +15,12 @@ import type {
PermissionRequestResult,
SessionEvent,
SessionEventHandler,
+ SessionEventPayload,
+ SessionEventType,
SessionHooks,
Tool,
ToolHandler,
+ TypedSessionEventHandler,
UserInputHandler,
UserInputRequest,
UserInputResponse,
@@ -53,6 +56,8 @@ export type AssistantMessageEvent = Extract = new Set();
+ private typedEventHandlers: Map void>> =
+ new Map();
private toolHandlers: Map = new Map();
private permissionHandler?: PermissionHandler;
private userInputHandler?: UserInputHandler;
@@ -190,7 +195,27 @@ export class CopilotSession {
* Events include assistant messages, tool executions, errors, and session state changes.
* Multiple handlers can be registered and will all receive events.
*
- * @param handler - A callback function that receives session events
+ * @param eventType - The specific event type to listen for (e.g., "assistant.message", "session.idle")
+ * @param handler - A callback function that receives events of the specified type
+ * @returns A function that, when called, unsubscribes the handler
+ *
+ * @example
+ * ```typescript
+ * // Listen for a specific event type
+ * const unsubscribe = session.on("assistant.message", (event) => {
+ * console.log("Assistant:", event.data.content);
+ * });
+ *
+ * // Later, to stop receiving events:
+ * unsubscribe();
+ * ```
+ */
+ on(eventType: K, handler: TypedSessionEventHandler): () => void;
+
+ /**
+ * Subscribes to all events from this session.
+ *
+ * @param handler - A callback function that receives all session events
* @returns A function that, when called, unsubscribes the handler
*
* @example
@@ -210,10 +235,34 @@ export class CopilotSession {
* unsubscribe();
* ```
*/
- on(handler: SessionEventHandler): () => void {
- this.eventHandlers.add(handler);
+ on(handler: SessionEventHandler): () => void;
+
+ on(
+ eventTypeOrHandler: K | SessionEventHandler,
+ handler?: TypedSessionEventHandler
+ ): () => void {
+ // Overload 1: on(eventType, handler) - typed event subscription
+ if (typeof eventTypeOrHandler === "string" && handler) {
+ const eventType = eventTypeOrHandler;
+ if (!this.typedEventHandlers.has(eventType)) {
+ this.typedEventHandlers.set(eventType, new Set());
+ }
+ // Cast is safe: handler receives the correctly typed event at dispatch time
+ const storedHandler = handler as (event: SessionEvent) => void;
+ this.typedEventHandlers.get(eventType)!.add(storedHandler);
+ return () => {
+ const handlers = this.typedEventHandlers.get(eventType);
+ if (handlers) {
+ handlers.delete(storedHandler);
+ }
+ };
+ }
+
+ // Overload 2: on(handler) - wildcard subscription
+ const wildcardHandler = eventTypeOrHandler as SessionEventHandler;
+ this.eventHandlers.add(wildcardHandler);
return () => {
- this.eventHandlers.delete(handler);
+ this.eventHandlers.delete(wildcardHandler);
};
}
@@ -224,6 +273,19 @@ export class CopilotSession {
* @internal This method is for internal use by the SDK.
*/
_dispatchEvent(event: SessionEvent): void {
+ // Dispatch to typed handlers for this specific event type
+ const typedHandlers = this.typedEventHandlers.get(event.type);
+ if (typedHandlers) {
+ for (const handler of typedHandlers) {
+ try {
+ handler(event as SessionEventPayload);
+ } catch (_error) {
+ // Handler error
+ }
+ }
+ }
+
+ // Dispatch to wildcard handlers
for (const handler of this.eventHandlers) {
try {
handler(event);
@@ -441,6 +503,7 @@ export class CopilotSession {
sessionId: this.sessionId,
});
this.eventHandlers.clear();
+ this.typedEventHandlers.clear();
this.toolHandlers.clear();
this.permissionHandler = undefined;
}
diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts
index 697e4a9f..93fa1d7f 100644
--- a/nodejs/src/types.ts
+++ b/nodejs/src/types.ts
@@ -807,7 +807,24 @@ export interface MessageOptions {
}
/**
- * Event handler callback type
+ * All possible event type strings from SessionEvent
+ */
+export type SessionEventType = SessionEvent["type"];
+
+/**
+ * Extract the specific event payload for a given event type
+ */
+export type SessionEventPayload = Extract;
+
+/**
+ * Event handler for a specific event type
+ */
+export type TypedSessionEventHandler = (
+ event: SessionEventPayload
+) => void;
+
+/**
+ * Event handler callback type (for all events)
*/
export type SessionEventHandler = (event: SessionEvent) => void;