🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@github/copilot-sdk

Package Overview
Dependencies
Maintainers
21
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@github/copilot-sdk - npm Package Compare versions

Comparing version
0.1.19
to
0.1.20
+2
-0
dist/client.d.ts

@@ -312,2 +312,4 @@ import { CopilotSession } from "./session.js";

private handlePermissionRequest;
private handleUserInputRequest;
private handleHooksInvoke;
private normalizeToolResult;

@@ -314,0 +316,0 @@ private isToolResultObject;

@@ -56,2 +56,7 @@ import { spawn } from "node:child_process";

}
if (options.cliUrl && (options.githubToken || options.useLoggedInUser !== void 0)) {
throw new Error(
"githubToken and useLoggedInUser cannot be used with cliUrl (external server manages its own auth)"
);
}
if (options.cliUrl) {

@@ -74,3 +79,6 @@ const { host, port } = this.parseCliUrl(options.cliUrl);

autoRestart: options.autoRestart ?? true,
env: options.env ?? process.env
env: options.env ?? process.env,
githubToken: options.githubToken,
// Default useLoggedInUser to false when githubToken is provided, otherwise true
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true)
};

@@ -322,2 +330,5 @@ }

requestPermission: !!config.onPermissionRequest,
requestUserInput: !!config.onUserInputRequest,
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
workingDirectory: config.workingDirectory,
streaming: config.streaming,

@@ -337,2 +348,8 @@ mcpServers: config.mcpServers,

}
if (config.onUserInputRequest) {
session.registerUserInputHandler(config.onUserInputRequest);
}
if (config.hooks) {
session.registerHooks(config.hooks);
}
this.sessions.set(sessionId, session);

@@ -381,2 +398,5 @@ return session;

requestPermission: !!config.onPermissionRequest,
requestUserInput: !!config.onUserInputRequest,
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
workingDirectory: config.workingDirectory,
streaming: config.streaming,

@@ -386,3 +406,4 @@ mcpServers: config.mcpServers,

skillDirectories: config.skillDirectories,
disabledSkills: config.disabledSkills
disabledSkills: config.disabledSkills,
disableResume: config.disableResume
});

@@ -395,2 +416,8 @@ const { sessionId: resumedSessionId, workspacePath } = response;

}
if (config.onUserInputRequest) {
session.registerUserInputHandler(config.onUserInputRequest);
}
if (config.hooks) {
session.registerHooks(config.hooks);
}
this.sessions.set(resumedSessionId, session);

@@ -582,4 +609,13 @@ return session;

}
if (this.options.githubToken) {
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
}
if (!this.options.useLoggedInUser) {
args.push("--no-auto-login");
}
const envWithoutNodeDebug = { ...this.options.env };
delete envWithoutNodeDebug.NODE_DEBUG;
if (this.options.githubToken) {
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
}
const isJsFile = this.options.cliPath.endsWith(".js");

@@ -718,2 +754,10 @@ const isAbsolutePath = this.options.cliPath.startsWith("/") || /^[a-zA-Z]:/.test(this.options.cliPath);

);
this.connection.onRequest(
"userInput.request",
async (params) => await this.handleUserInputRequest(params)
);
this.connection.onRequest(
"hooks.invoke",
async (params) => await this.handleHooksInvoke(params)
);
this.connection.onClose(() => {

@@ -792,2 +836,28 @@ if (this.state === "connected" && this.options.autoRestart) {

}
async handleUserInputRequest(params) {
if (!params || typeof params.sessionId !== "string" || typeof params.question !== "string") {
throw new Error("Invalid user input request payload");
}
const session = this.sessions.get(params.sessionId);
if (!session) {
throw new Error(`Session not found: ${params.sessionId}`);
}
const result = await session._handleUserInputRequest({
question: params.question,
choices: params.choices,
allowFreeform: params.allowFreeform
});
return result;
}
async handleHooksInvoke(params) {
if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
throw new Error("Invalid hooks invoke payload");
}
const session = this.sessions.get(params.sessionId);
if (!session) {
throw new Error(`Session not found: ${params.sessionId}`);
}
const output = await session._handleHooksInvoke(params.hookType, params.input);
return { output };
}
normalizeToolResult(result) {

@@ -794,0 +864,0 @@ if (result === void 0 || result === null) {

+1
-1

@@ -9,2 +9,2 @@ /**

export { defineTool } from "./types.js";
export type { ConnectionState, CopilotClientOptions, CustomAgentConfig, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ResumeSessionConfig, SessionConfig, SessionEvent, SessionEventHandler, SessionMetadata, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageReplaceConfig, Tool, ToolHandler, ToolInvocation, ToolResultObject, ZodSchema, } from "./types.js";
export type { ConnectionState, CopilotClientOptions, CustomAgentConfig, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ResumeSessionConfig, SessionConfig, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionMetadata, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageReplaceConfig, Tool, ToolHandler, ToolInvocation, ToolResultObject, TypedSessionEventHandler, ZodSchema, } from "./types.js";

@@ -6,3 +6,3 @@ /**

import type { MessageConnection } from "vscode-jsonrpc/node";
import type { MessageOptions, PermissionHandler, PermissionRequestResult, SessionEvent, SessionEventHandler, Tool, ToolHandler } from "./types.js";
import type { MessageOptions, PermissionHandler, PermissionRequestResult, SessionEvent, SessionEventHandler, SessionEventType, SessionHooks, Tool, ToolHandler, TypedSessionEventHandler, UserInputHandler, UserInputResponse } from "./types.js";
/** Assistant message event - the final response from the assistant. */

@@ -42,4 +42,7 @@ export type AssistantMessageEvent = Extract<SessionEvent, {

private eventHandlers;
private typedEventHandlers;
private toolHandlers;
private permissionHandler?;
private userInputHandler?;
private hooks?;
/**

@@ -109,3 +112,4 @@ * Creates a new CopilotSession instance.

*
* @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

@@ -115,2 +119,20 @@ *

* ```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<K extends SessionEventType>(eventType: K, handler: TypedSessionEventHandler<K>): () => 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
* ```typescript
* const unsubscribe = session.on((event) => {

@@ -168,2 +190,22 @@ * switch (event.type) {

/**
* Registers a user input handler for ask_user requests.
*
* When the agent needs input from the user (via ask_user tool),
* this handler is called to provide the response.
*
* @param handler - The user input handler function, or undefined to remove the handler
* @internal This method is typically called internally when creating a session.
*/
registerUserInputHandler(handler?: UserInputHandler): void;
/**
* Registers hook handlers for session lifecycle events.
*
* Hooks allow custom logic to be executed at various points during
* the session lifecycle (before/after tool use, session start/end, etc.).
*
* @param hooks - The hook handlers object, or undefined to remove all hooks
* @internal This method is typically called internally when creating a session.
*/
registerHooks(hooks?: SessionHooks): void;
/**
* Handles a permission request from the Copilot CLI.

@@ -177,2 +219,19 @@ *

/**
* Handles a user input request from the Copilot CLI.
*
* @param request - The user input request data from the CLI
* @returns A promise that resolves with the user's response
* @internal This method is for internal use by the SDK.
*/
_handleUserInputRequest(request: unknown): Promise<UserInputResponse>;
/**
* Handles a hooks invocation from the Copilot CLI.
*
* @param hookType - The type of hook being invoked
* @param input - The input data for the hook
* @returns A promise that resolves with the hook output, or undefined
* @internal This method is for internal use by the SDK.
*/
_handleHooksInvoke(hookType: string, input: unknown): Promise<unknown>;
/**
* Retrieves all events and messages from this session's history.

@@ -179,0 +238,0 @@ *

@@ -16,4 +16,7 @@ class CopilotSession {

eventHandlers = /* @__PURE__ */ new Set();
typedEventHandlers = /* @__PURE__ */ new Map();
toolHandlers = /* @__PURE__ */ new Map();
permissionHandler;
userInputHandler;
hooks;
/**

@@ -115,32 +118,21 @@ * Path to the session workspace directory when infinite sessions are enabled.

}
/**
* Subscribes to events from this session.
*
* 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
* @returns A function that, when called, unsubscribes the handler
*
* @example
* ```typescript
* const unsubscribe = session.on((event) => {
* switch (event.type) {
* case "assistant.message":
* console.log("Assistant:", event.data.content);
* break;
* case "session.error":
* console.error("Error:", event.data.message);
* break;
* }
* });
*
* // Later, to stop receiving events:
* unsubscribe();
* ```
*/
on(handler) {
this.eventHandlers.add(handler);
on(eventTypeOrHandler, handler) {
if (typeof eventTypeOrHandler === "string" && handler) {
const eventType = eventTypeOrHandler;
if (!this.typedEventHandlers.has(eventType)) {
this.typedEventHandlers.set(eventType, /* @__PURE__ */ new Set());
}
const storedHandler = handler;
this.typedEventHandlers.get(eventType).add(storedHandler);
return () => {
const handlers = this.typedEventHandlers.get(eventType);
if (handlers) {
handlers.delete(storedHandler);
}
};
}
const wildcardHandler = eventTypeOrHandler;
this.eventHandlers.add(wildcardHandler);
return () => {
this.eventHandlers.delete(handler);
this.eventHandlers.delete(wildcardHandler);
};

@@ -155,2 +147,11 @@ }

_dispatchEvent(event) {
const typedHandlers = this.typedEventHandlers.get(event.type);
if (typedHandlers) {
for (const handler of typedHandlers) {
try {
handler(event);
} catch (_error) {
}
}
}
for (const handler of this.eventHandlers) {

@@ -204,2 +205,26 @@ try {

/**
* Registers a user input handler for ask_user requests.
*
* When the agent needs input from the user (via ask_user tool),
* this handler is called to provide the response.
*
* @param handler - The user input handler function, or undefined to remove the handler
* @internal This method is typically called internally when creating a session.
*/
registerUserInputHandler(handler) {
this.userInputHandler = handler;
}
/**
* Registers hook handlers for session lifecycle events.
*
* Hooks allow custom logic to be executed at various points during
* the session lifecycle (before/after tool use, session start/end, etc.).
*
* @param hooks - The hook handlers object, or undefined to remove all hooks
* @internal This method is typically called internally when creating a session.
*/
registerHooks(hooks) {
this.hooks = hooks;
}
/**
* Handles a permission request from the Copilot CLI.

@@ -225,2 +250,53 @@ *

/**
* Handles a user input request from the Copilot CLI.
*
* @param request - The user input request data from the CLI
* @returns A promise that resolves with the user's response
* @internal This method is for internal use by the SDK.
*/
async _handleUserInputRequest(request) {
if (!this.userInputHandler) {
throw new Error("User input requested but no handler registered");
}
try {
const result = await this.userInputHandler(request, {
sessionId: this.sessionId
});
return result;
} catch (error) {
throw error;
}
}
/**
* Handles a hooks invocation from the Copilot CLI.
*
* @param hookType - The type of hook being invoked
* @param input - The input data for the hook
* @returns A promise that resolves with the hook output, or undefined
* @internal This method is for internal use by the SDK.
*/
async _handleHooksInvoke(hookType, input) {
if (!this.hooks) {
return void 0;
}
const handlerMap = {
preToolUse: this.hooks.onPreToolUse,
postToolUse: this.hooks.onPostToolUse,
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
sessionStart: this.hooks.onSessionStart,
sessionEnd: this.hooks.onSessionEnd,
errorOccurred: this.hooks.onErrorOccurred
};
const handler = handlerMap[hookType];
if (!handler) {
return void 0;
}
try {
const result = await handler(input, { sessionId: this.sessionId });
return result;
} catch (_error) {
return void 0;
}
}
/**
* Retrieves all events and messages from this session's history.

@@ -271,2 +347,3 @@ *

this.eventHandlers.clear();
this.typedEventHandlers.clear();
this.toolHandlers.clear();

@@ -273,0 +350,0 @@ this.permissionHandler = void 0;

@@ -61,2 +61,15 @@ /**

env?: Record<string, string | undefined>;
/**
* GitHub token to use for authentication.
* When provided, the token is passed to the CLI server via environment variable.
* This takes priority over other authentication methods.
*/
githubToken?: string;
/**
* Whether to use the logged-in user for authentication.
* When true, the CLI server will attempt to use stored OAuth tokens or gh CLI auth.
* When false, only explicit tokens (githubToken or environment variables) are used.
* @default true (but defaults to false when githubToken is provided)
*/
useLoggedInUser?: boolean;
}

@@ -171,2 +184,205 @@ /**

/**
* Request for user input from the agent (enables ask_user tool)
*/
export interface UserInputRequest {
/**
* The question to ask the user
*/
question: string;
/**
* Optional choices for multiple choice questions
*/
choices?: string[];
/**
* Whether to allow freeform text input in addition to choices
* @default true
*/
allowFreeform?: boolean;
}
/**
* Response to a user input request
*/
export interface UserInputResponse {
/**
* The user's answer
*/
answer: string;
/**
* Whether the answer was freeform (not from choices)
*/
wasFreeform: boolean;
}
/**
* Handler for user input requests from the agent
*/
export type UserInputHandler = (request: UserInputRequest, invocation: {
sessionId: string;
}) => Promise<UserInputResponse> | UserInputResponse;
/**
* Base interface for all hook inputs
*/
export interface BaseHookInput {
timestamp: number;
cwd: string;
}
/**
* Input for pre-tool-use hook
*/
export interface PreToolUseHookInput extends BaseHookInput {
toolName: string;
toolArgs: unknown;
}
/**
* Output for pre-tool-use hook
*/
export interface PreToolUseHookOutput {
permissionDecision?: "allow" | "deny" | "ask";
permissionDecisionReason?: string;
modifiedArgs?: unknown;
additionalContext?: string;
suppressOutput?: boolean;
}
/**
* Handler for pre-tool-use hook
*/
export type PreToolUseHandler = (input: PreToolUseHookInput, invocation: {
sessionId: string;
}) => Promise<PreToolUseHookOutput | void> | PreToolUseHookOutput | void;
/**
* Input for post-tool-use hook
*/
export interface PostToolUseHookInput extends BaseHookInput {
toolName: string;
toolArgs: unknown;
toolResult: ToolResultObject;
}
/**
* Output for post-tool-use hook
*/
export interface PostToolUseHookOutput {
modifiedResult?: ToolResultObject;
additionalContext?: string;
suppressOutput?: boolean;
}
/**
* Handler for post-tool-use hook
*/
export type PostToolUseHandler = (input: PostToolUseHookInput, invocation: {
sessionId: string;
}) => Promise<PostToolUseHookOutput | void> | PostToolUseHookOutput | void;
/**
* Input for user-prompt-submitted hook
*/
export interface UserPromptSubmittedHookInput extends BaseHookInput {
prompt: string;
}
/**
* Output for user-prompt-submitted hook
*/
export interface UserPromptSubmittedHookOutput {
modifiedPrompt?: string;
additionalContext?: string;
suppressOutput?: boolean;
}
/**
* Handler for user-prompt-submitted hook
*/
export type UserPromptSubmittedHandler = (input: UserPromptSubmittedHookInput, invocation: {
sessionId: string;
}) => Promise<UserPromptSubmittedHookOutput | void> | UserPromptSubmittedHookOutput | void;
/**
* Input for session-start hook
*/
export interface SessionStartHookInput extends BaseHookInput {
source: "startup" | "resume" | "new";
initialPrompt?: string;
}
/**
* Output for session-start hook
*/
export interface SessionStartHookOutput {
additionalContext?: string;
modifiedConfig?: Record<string, unknown>;
}
/**
* Handler for session-start hook
*/
export type SessionStartHandler = (input: SessionStartHookInput, invocation: {
sessionId: string;
}) => Promise<SessionStartHookOutput | void> | SessionStartHookOutput | void;
/**
* Input for session-end hook
*/
export interface SessionEndHookInput extends BaseHookInput {
reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
finalMessage?: string;
error?: string;
}
/**
* Output for session-end hook
*/
export interface SessionEndHookOutput {
suppressOutput?: boolean;
cleanupActions?: string[];
sessionSummary?: string;
}
/**
* Handler for session-end hook
*/
export type SessionEndHandler = (input: SessionEndHookInput, invocation: {
sessionId: string;
}) => Promise<SessionEndHookOutput | void> | SessionEndHookOutput | void;
/**
* Input for error-occurred hook
*/
export interface ErrorOccurredHookInput extends BaseHookInput {
error: string;
errorContext: "model_call" | "tool_execution" | "system" | "user_input";
recoverable: boolean;
}
/**
* Output for error-occurred hook
*/
export interface ErrorOccurredHookOutput {
suppressOutput?: boolean;
errorHandling?: "retry" | "skip" | "abort";
retryCount?: number;
userNotification?: string;
}
/**
* Handler for error-occurred hook
*/
export type ErrorOccurredHandler = (input: ErrorOccurredHookInput, invocation: {
sessionId: string;
}) => Promise<ErrorOccurredHookOutput | void> | ErrorOccurredHookOutput | void;
/**
* Configuration for session hooks
*/
export interface SessionHooks {
/**
* Called before a tool is executed
*/
onPreToolUse?: PreToolUseHandler;
/**
* Called after a tool is executed
*/
onPostToolUse?: PostToolUseHandler;
/**
* Called when the user submits a prompt
*/
onUserPromptSubmitted?: UserPromptSubmittedHandler;
/**
* Called when a session starts
*/
onSessionStart?: SessionStartHandler;
/**
* Called when a session ends
*/
onSessionEnd?: SessionEndHandler;
/**
* Called when an error occurs
*/
onErrorOccurred?: ErrorOccurredHandler;
}
/**
* Base interface for MCP server configuration.

@@ -323,2 +539,17 @@ */

onPermissionRequest?: PermissionHandler;
/**
* Handler for user input requests from the agent.
* When provided, enables the ask_user tool allowing the agent to ask questions.
*/
onUserInputRequest?: UserInputHandler;
/**
* Hook handlers for intercepting session lifecycle events.
* When provided, enables hooks callback allowing custom logic at various points.
*/
hooks?: SessionHooks;
/**
* Working directory for the session.
* Tool operations will be relative to this directory.
*/
workingDirectory?: string;
streaming?: boolean;

@@ -352,3 +583,10 @@ /**

*/
export type ResumeSessionConfig = Pick<SessionConfig, "tools" | "provider" | "streaming" | "onPermissionRequest" | "mcpServers" | "customAgents" | "skillDirectories" | "disabledSkills">;
export type ResumeSessionConfig = Pick<SessionConfig, "tools" | "provider" | "streaming" | "onPermissionRequest" | "onUserInputRequest" | "hooks" | "workingDirectory" | "mcpServers" | "customAgents" | "skillDirectories" | "disabledSkills"> & {
/**
* When true, skips emitting the session.resume event.
* Useful for reconnecting to a session without triggering resume-related side effects.
* @default false
*/
disableResume?: boolean;
};
/**

@@ -414,4 +652,18 @@ * Configuration for a custom API provider.

/**
* 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<T extends SessionEventType> = Extract<SessionEvent, {
type: T;
}>;
/**
* Event handler for a specific event type
*/
export type TypedSessionEventHandler<T extends SessionEventType> = (event: SessionEventPayload<T>) => void;
/**
* Event handler callback type (for all events)
*/
export type SessionEventHandler = (event: SessionEvent) => void;

@@ -418,0 +670,0 @@ /**

@@ -7,3 +7,3 @@ {

},
"version": "0.1.19",
"version": "0.1.20",
"description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC",

@@ -44,3 +44,3 @@ "main": "./dist/index.js",

"dependencies": {
"@github/copilot": "^0.0.394",
"@github/copilot": "^0.0.399",
"vscode-jsonrpc": "^8.2.1",

@@ -47,0 +47,0 @@ "zod": "^4.3.5"

+218
-30

@@ -27,11 +27,10 @@ # Copilot SDK for Node.js/TypeScript

// Wait for response using session.idle event
// Wait for response using typed event handlers
const done = new Promise<void>((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();
});
});

@@ -68,2 +67,4 @@

- `autoRestart?: boolean` - Auto-restart on crash (default: true)
- `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`.

@@ -91,6 +92,9 @@ #### Methods

- `sessionId?: string` - Custom session ID
- `model?: string` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.)
- `model?: string` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.**
- `tools?: Tool[]` - Custom tools exposed to the CLI
- `systemMessage?: SystemMessageConfig` - System message customization (see below)
- `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below)
- `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section.
- `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section.
- `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.

@@ -160,9 +164,30 @@ ##### `resumeSession(sessionId: string, config?: ResumeSessionConfig): Promise<CopilotSession>`

##### `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);
});

@@ -233,24 +258,30 @@

// Wait for completion using session.idle event
// Wait for completion using typed event handlers
const done = new Promise<void>((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();
});
});

@@ -415,2 +446,159 @@

### Custom Providers
The SDK supports custom OpenAI-compatible API providers (BYOK - Bring Your Own Key), including local providers like Ollama. When using a custom provider, you must specify the `model` explicitly.
**ProviderConfig:**
- `type?: "openai" | "azure" | "anthropic"` - Provider type (default: "openai")
- `baseUrl: string` - API endpoint URL (required)
- `apiKey?: string` - API key (optional for local providers like Ollama)
- `bearerToken?: string` - Bearer token for authentication (takes precedence over apiKey)
- `wireApi?: "completions" | "responses"` - API format for OpenAI/Azure (default: "completions")
- `azure?.apiVersion?: string` - Azure API version (default: "2024-10-21")
**Example with Ollama:**
```typescript
const session = await client.createSession({
model: "deepseek-coder-v2:16b", // Required when using custom provider
provider: {
type: "openai",
baseUrl: "http://localhost:11434/v1", // Ollama endpoint
// apiKey not required for Ollama
},
});
await session.sendAndWait({ prompt: "Hello!" });
```
**Example with custom OpenAI-compatible API:**
```typescript
const session = await client.createSession({
model: "gpt-4",
provider: {
type: "openai",
baseUrl: "https://my-api.example.com/v1",
apiKey: process.env.MY_API_KEY,
},
});
```
**Example with Azure OpenAI:**
```typescript
const session = await client.createSession({
model: "gpt-4",
provider: {
type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
baseUrl: "https://my-resource.openai.azure.com", // Just the host, no path
apiKey: process.env.AZURE_OPENAI_KEY,
azure: {
apiVersion: "2024-10-21",
},
},
});
```
> **Important notes:**
> - When using a custom provider, the `model` parameter is **required**. The SDK will throw an error if no model is specified.
> - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`.
> - 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.
## User Input Requests
Enable the agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler:
```typescript
const session = await client.createSession({
model: "gpt-5",
onUserInputRequest: async (request, invocation) => {
// request.question - The question to ask
// request.choices - Optional array of choices for multiple choice
// request.allowFreeform - Whether freeform input is allowed (default: true)
console.log(`Agent asks: ${request.question}`);
if (request.choices) {
console.log(`Choices: ${request.choices.join(", ")}`);
}
// Return the user's response
return {
answer: "User's answer here",
wasFreeform: true, // Whether the answer was freeform (not from choices)
};
},
});
```
## Session Hooks
Hook into session lifecycle events by providing handlers in the `hooks` configuration:
```typescript
const session = await client.createSession({
model: "gpt-5",
hooks: {
// Called before each tool execution
onPreToolUse: async (input, invocation) => {
console.log(`About to run tool: ${input.toolName}`);
// Return permission decision and optionally modify args
return {
permissionDecision: "allow", // "allow", "deny", or "ask"
modifiedArgs: input.toolArgs, // Optionally modify tool arguments
additionalContext: "Extra context for the model",
};
},
// Called after each tool execution
onPostToolUse: async (input, invocation) => {
console.log(`Tool ${input.toolName} completed`);
// Optionally modify the result or add context
return {
additionalContext: "Post-execution notes",
};
},
// Called when user submits a prompt
onUserPromptSubmitted: async (input, invocation) => {
console.log(`User prompt: ${input.prompt}`);
return {
modifiedPrompt: input.prompt, // Optionally modify the prompt
};
},
// Called when session starts
onSessionStart: async (input, invocation) => {
console.log(`Session started from: ${input.source}`); // "startup", "resume", "new"
return {
additionalContext: "Session initialization context",
};
},
// Called when session ends
onSessionEnd: async (input, invocation) => {
console.log(`Session ended: ${input.reason}`);
},
// Called when an error occurs
onErrorOccurred: async (input, invocation) => {
console.error(`Error in ${input.errorContext}: ${input.error}`);
return {
errorHandling: "retry", // "retry", "skip", or "abort"
};
},
},
});
```
**Available hooks:**
- `onPreToolUse` - Intercept tool calls before execution. Can allow/deny or modify arguments.
- `onPostToolUse` - Process tool results after execution. Can modify results or add context.
- `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing.
- `onSessionStart` - Run logic when a session starts or resumes.
- `onSessionEnd` - Cleanup or logging when session ends.
- `onErrorOccurred` - Handle errors with retry/skip/abort strategies.
## Error Handling

@@ -417,0 +605,0 @@