@github/copilot-sdk
Advanced tools
+2
-0
@@ -312,2 +312,4 @@ import { CopilotSession } from "./session.js"; | ||
| private handlePermissionRequest; | ||
| private handleUserInputRequest; | ||
| private handleHooksInvoke; | ||
| private normalizeToolResult; | ||
@@ -314,0 +316,0 @@ private isToolResultObject; |
+72
-2
@@ -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"; |
+61
-2
@@ -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 @@ * |
+106
-29
@@ -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; |
+254
-2
@@ -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 @@ /** |
+2
-2
@@ -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 @@ |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
118988
23.01%3153
17.08%617
43.82%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated