@github/copilot-sdk 0.2.2 → 0.3.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  */
6
6
  export { CopilotClient } from "./client.js";
7
7
  export { CopilotSession, type AssistantMessageEvent } from "./session.js";
8
- export { defineTool, approveAll, SYSTEM_PROMPT_SECTIONS } from "./types.js";
9
- export type { CommandContext, CommandDefinition, CommandHandler, ConnectionState, CopilotClientOptions, CustomAgentConfig, ElicitationFieldValue, ElicitationHandler, ElicitationParams, ElicitationContext, ElicitationResult, ElicitationSchema, ElicitationSchemaField, ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, InputOptions, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelCapabilitiesOverride, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ResumeSessionConfig, SectionOverride, SectionOverrideAction, SectionTransformFn, SessionCapabilities, SessionConfig, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionLifecycleEvent, SessionLifecycleEventType, SessionLifecycleHandler, SessionContext, SessionListFilter, SessionMetadata, SessionUiApi, SessionFsConfig, SessionFsHandler, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageCustomizeConfig, SystemMessageReplaceConfig, SystemPromptSection, TelemetryConfig, TraceContext, TraceContextProvider, Tool, ToolHandler, ToolInvocation, ToolResultObject, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, } from "./types.js";
8
+ export { defineTool, approveAll, convertMcpCallToolResult, createSessionFsAdapter, SYSTEM_PROMPT_SECTIONS, } from "./types.js";
9
+ export type { CommandContext, CommandDefinition, CommandHandler, ConnectionState, CopilotClientOptions, CustomAgentConfig, ElicitationFieldValue, ElicitationHandler, ElicitationParams, ElicitationContext, ElicitationResult, ElicitationSchema, ElicitationSchemaField, ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, InputOptions, MCPStdioServerConfig, MCPHTTPServerConfig, MCPServerConfig, DefaultAgentConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelCapabilitiesOverride, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ProviderConfig, ResumeSessionConfig, SectionOverride, SectionOverrideAction, SectionTransformFn, SessionCapabilities, SessionConfig, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionLifecycleEvent, SessionLifecycleEventType, SessionLifecycleHandler, SessionContext, SessionListFilter, SessionMetadata, SessionUiApi, SessionFsConfig, SessionFsProvider, SessionFsFileInfo, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageCustomizeConfig, SystemMessageReplaceConfig, SystemPromptSection, TelemetryConfig, TraceContext, TraceContextProvider, Tool, ToolHandler, ToolInvocation, ToolResultObject, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,10 +1,18 @@
1
1
  import { CopilotClient } from "./client.js";
2
2
  import { CopilotSession } from "./session.js";
3
- import { defineTool, approveAll, SYSTEM_PROMPT_SECTIONS } from "./types.js";
3
+ import {
4
+ defineTool,
5
+ approveAll,
6
+ convertMcpCallToolResult,
7
+ createSessionFsAdapter,
8
+ SYSTEM_PROMPT_SECTIONS
9
+ } from "./types.js";
4
10
  export {
5
11
  CopilotClient,
6
12
  CopilotSession,
7
13
  SYSTEM_PROMPT_SECTIONS,
8
14
  approveAll,
15
+ convertMcpCallToolResult,
16
+ createSessionFsAdapter,
9
17
  defineTool
10
18
  };
package/dist/session.js CHANGED
@@ -100,7 +100,8 @@ class CopilotSession {
100
100
  sessionId: this.sessionId,
101
101
  prompt: options.prompt,
102
102
  attachments: options.attachments,
103
- mode: options.mode
103
+ mode: options.mode,
104
+ requestHeaders: options.requestHeaders
104
105
  });
105
106
  return response.messageId;
106
107
  }
@@ -0,0 +1,44 @@
1
+ import type { SessionFsHandler, SessionFsStatResult, SessionFsReaddirWithTypesEntry } from "./generated/rpc.js";
2
+ /**
3
+ * File metadata returned by {@link SessionFsProvider.stat}.
4
+ * Same shape as the generated {@link SessionFsStatResult} but without the
5
+ * `error` field, since providers signal errors by throwing.
6
+ */
7
+ export type SessionFsFileInfo = Omit<SessionFsStatResult, "error">;
8
+ /**
9
+ * Interface for session filesystem providers. Implementors use idiomatic
10
+ * TypeScript patterns: throw on error, return values directly. Use
11
+ * {@link createSessionFsAdapter} to convert a provider into the
12
+ * {@link SessionFsHandler} expected by the SDK.
13
+ *
14
+ * Errors with a `code` property of `"ENOENT"` are mapped to the ENOENT
15
+ * error code; all others map to UNKNOWN.
16
+ */
17
+ export interface SessionFsProvider {
18
+ /** Reads the full content of a file. Throw if the file does not exist. */
19
+ readFile(path: string): Promise<string>;
20
+ /** Writes content to a file, creating parent directories if needed. */
21
+ writeFile(path: string, content: string, mode?: number): Promise<void>;
22
+ /** Appends content to a file, creating parent directories if needed. */
23
+ appendFile(path: string, content: string, mode?: number): Promise<void>;
24
+ /** Checks whether a path exists. */
25
+ exists(path: string): Promise<boolean>;
26
+ /** Gets metadata about a file or directory. Throw if it does not exist. */
27
+ stat(path: string): Promise<SessionFsFileInfo>;
28
+ /** Creates a directory. If recursive is true, creates parents as needed. */
29
+ mkdir(path: string, recursive: boolean, mode?: number): Promise<void>;
30
+ /** Lists entry names in a directory. Throw if it does not exist. */
31
+ readdir(path: string): Promise<string[]>;
32
+ /** Lists entries with type info. Throw if the directory does not exist. */
33
+ readdirWithTypes(path: string): Promise<SessionFsReaddirWithTypesEntry[]>;
34
+ /** Removes a file or directory. If force is true, do not throw on ENOENT. */
35
+ rm(path: string, recursive: boolean, force: boolean): Promise<void>;
36
+ /** Renames/moves a file or directory. */
37
+ rename(src: string, dest: string): Promise<void>;
38
+ }
39
+ /**
40
+ * Wraps a {@link SessionFsProvider} into the {@link SessionFsHandler}
41
+ * interface expected by the SDK, converting thrown errors into
42
+ * {@link SessionFsError} results.
43
+ */
44
+ export declare function createSessionFsAdapter(provider: SessionFsProvider): SessionFsHandler;
@@ -0,0 +1,97 @@
1
+ function createSessionFsAdapter(provider) {
2
+ return {
3
+ readFile: async ({ path }) => {
4
+ try {
5
+ const content = await provider.readFile(path);
6
+ return { content };
7
+ } catch (err) {
8
+ return { content: "", error: toSessionFsError(err) };
9
+ }
10
+ },
11
+ writeFile: async ({ path, content, mode }) => {
12
+ try {
13
+ await provider.writeFile(path, content, mode);
14
+ return void 0;
15
+ } catch (err) {
16
+ return toSessionFsError(err);
17
+ }
18
+ },
19
+ appendFile: async ({ path, content, mode }) => {
20
+ try {
21
+ await provider.appendFile(path, content, mode);
22
+ return void 0;
23
+ } catch (err) {
24
+ return toSessionFsError(err);
25
+ }
26
+ },
27
+ exists: async ({ path }) => {
28
+ try {
29
+ return { exists: await provider.exists(path) };
30
+ } catch {
31
+ return { exists: false };
32
+ }
33
+ },
34
+ stat: async ({ path }) => {
35
+ try {
36
+ return await provider.stat(path);
37
+ } catch (err) {
38
+ return {
39
+ isFile: false,
40
+ isDirectory: false,
41
+ size: 0,
42
+ mtime: (/* @__PURE__ */ new Date()).toISOString(),
43
+ birthtime: (/* @__PURE__ */ new Date()).toISOString(),
44
+ error: toSessionFsError(err)
45
+ };
46
+ }
47
+ },
48
+ mkdir: async ({ path, recursive, mode }) => {
49
+ try {
50
+ await provider.mkdir(path, recursive ?? false, mode);
51
+ return void 0;
52
+ } catch (err) {
53
+ return toSessionFsError(err);
54
+ }
55
+ },
56
+ readdir: async ({ path }) => {
57
+ try {
58
+ const entries = await provider.readdir(path);
59
+ return { entries };
60
+ } catch (err) {
61
+ return { entries: [], error: toSessionFsError(err) };
62
+ }
63
+ },
64
+ readdirWithTypes: async ({ path }) => {
65
+ try {
66
+ const entries = await provider.readdirWithTypes(path);
67
+ return { entries };
68
+ } catch (err) {
69
+ return { entries: [], error: toSessionFsError(err) };
70
+ }
71
+ },
72
+ rm: async ({ path, recursive, force }) => {
73
+ try {
74
+ await provider.rm(path, recursive ?? false, force ?? false);
75
+ return void 0;
76
+ } catch (err) {
77
+ return toSessionFsError(err);
78
+ }
79
+ },
80
+ rename: async ({ src, dest }) => {
81
+ try {
82
+ await provider.rename(src, dest);
83
+ return void 0;
84
+ } catch (err) {
85
+ return toSessionFsError(err);
86
+ }
87
+ }
88
+ };
89
+ }
90
+ function toSessionFsError(err) {
91
+ const e = err;
92
+ const code = e.code === "ENOENT" ? "ENOENT" : "UNKNOWN";
93
+ return { code, message: e.message ?? String(err) };
94
+ }
95
+ export {
96
+ createSessionFsAdapter
97
+ };
package/dist/types.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Type definitions for the Copilot SDK
3
3
  */
4
- import type { SessionFsHandler } from "./generated/rpc.js";
4
+ import type { SessionFsProvider } from "./sessionFsProvider.js";
5
5
  import type { SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
6
6
  import type { CopilotSession } from "./session.js";
7
7
  export type SessionEvent = GeneratedSessionEvent;
8
- export type { SessionFsHandler } from "./generated/rpc.js";
8
+ export type { SessionFsProvider } from "./sessionFsProvider.js";
9
+ export { createSessionFsAdapter } from "./sessionFsProvider.js";
10
+ export type { SessionFsFileInfo } from "./sessionFsProvider.js";
9
11
  /**
10
12
  * Options for creating a CopilotClient
11
13
  */
@@ -176,6 +178,40 @@ export type ToolResultObject = {
176
178
  toolTelemetry?: Record<string, unknown>;
177
179
  };
178
180
  export type ToolResult = string | ToolResultObject;
181
+ /**
182
+ * Content block types within an MCP CallToolResult.
183
+ */
184
+ type McpCallToolResultTextContent = {
185
+ type: "text";
186
+ text: string;
187
+ };
188
+ type McpCallToolResultImageContent = {
189
+ type: "image";
190
+ data: string;
191
+ mimeType: string;
192
+ };
193
+ type McpCallToolResultResourceContent = {
194
+ type: "resource";
195
+ resource: {
196
+ uri: string;
197
+ mimeType?: string;
198
+ text?: string;
199
+ blob?: string;
200
+ };
201
+ };
202
+ type McpCallToolResultContent = McpCallToolResultTextContent | McpCallToolResultImageContent | McpCallToolResultResourceContent;
203
+ /**
204
+ * MCP-compatible CallToolResult type. Can be passed to
205
+ * {@link convertMcpCallToolResult} to produce a {@link ToolResultObject}.
206
+ */
207
+ type McpCallToolResult = {
208
+ content: McpCallToolResultContent[];
209
+ isError?: boolean;
210
+ };
211
+ /**
212
+ * Converts an MCP CallToolResult into the SDK's ToolResultObject format.
213
+ */
214
+ export declare function convertMcpCallToolResult(callResult: McpCallToolResult): ToolResultObject;
179
215
  export interface ToolInvocation {
180
216
  sessionId: string;
181
217
  toolCallId: string;
@@ -532,18 +568,18 @@ export type SystemMessageConfig = SystemMessageAppendConfig | SystemMessageRepla
532
568
  * Permission request types from the server
533
569
  */
534
570
  export interface PermissionRequest {
535
- kind: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool";
571
+ kind: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool" | "memory" | "hook";
536
572
  toolCallId?: string;
537
- [key: string]: unknown;
538
573
  }
539
- import type { SessionPermissionsHandlePendingPermissionRequestParams } from "./generated/rpc.js";
540
- export type PermissionRequestResult = SessionPermissionsHandlePendingPermissionRequestParams["result"] | {
574
+ import type { PermissionDecisionRequest } from "./generated/rpc.js";
575
+ export type PermissionRequestResult = PermissionDecisionRequest["result"] | {
541
576
  kind: "no-result";
542
577
  };
543
578
  export type PermissionHandler = (request: PermissionRequest, invocation: {
544
579
  sessionId: string;
545
580
  }) => Promise<PermissionRequestResult> | PermissionRequestResult;
546
581
  export declare const approveAll: PermissionHandler;
582
+ export declare const defaultJoinSessionPermissionHandler: PermissionHandler;
547
583
  /**
548
584
  * Request for user input from the agent (enables ask_user tool)
549
585
  */
@@ -756,8 +792,8 @@ interface MCPServerConfigBase {
756
792
  */
757
793
  tools: string[];
758
794
  /**
759
- * Indicates "remote" or "local" server type.
760
- * If not specified, defaults to "local".
795
+ * Indicates the server type: "stdio" for local/subprocess servers, "http"/"sse" for remote servers.
796
+ * If not specified, defaults to "stdio".
761
797
  */
762
798
  type?: string;
763
799
  /**
@@ -768,7 +804,7 @@ interface MCPServerConfigBase {
768
804
  /**
769
805
  * Configuration for a local/stdio MCP server.
770
806
  */
771
- export interface MCPLocalServerConfig extends MCPServerConfigBase {
807
+ export interface MCPStdioServerConfig extends MCPServerConfigBase {
772
808
  type?: "local" | "stdio";
773
809
  command: string;
774
810
  args: string[];
@@ -781,7 +817,7 @@ export interface MCPLocalServerConfig extends MCPServerConfigBase {
781
817
  /**
782
818
  * Configuration for a remote MCP server (HTTP or SSE).
783
819
  */
784
- export interface MCPRemoteServerConfig extends MCPServerConfigBase {
820
+ export interface MCPHTTPServerConfig extends MCPServerConfigBase {
785
821
  type: "http" | "sse";
786
822
  /**
787
823
  * URL of the remote server.
@@ -795,7 +831,7 @@ export interface MCPRemoteServerConfig extends MCPServerConfigBase {
795
831
  /**
796
832
  * Union type for MCP server configurations.
797
833
  */
798
- export type MCPServerConfig = MCPLocalServerConfig | MCPRemoteServerConfig;
834
+ export type MCPServerConfig = MCPStdioServerConfig | MCPHTTPServerConfig;
799
835
  /**
800
836
  * Configuration for a custom agent.
801
837
  */
@@ -830,6 +866,28 @@ export interface CustomAgentConfig {
830
866
  * @default true
831
867
  */
832
868
  infer?: boolean;
869
+ /**
870
+ * List of skill names to preload into this agent's context.
871
+ * When set, the full content of each listed skill is eagerly injected into
872
+ * the agent's context at startup. Skills are resolved by name from the
873
+ * session's configured skill directories (`skillDirectories`).
874
+ * When omitted, no skills are injected (opt-in model).
875
+ */
876
+ skills?: string[];
877
+ }
878
+ /**
879
+ * Configuration for the default agent (the built-in agent that handles
880
+ * turns when no custom agent is selected).
881
+ * Use this to control tool visibility for the default agent independently of custom sub-agents.
882
+ */
883
+ export interface DefaultAgentConfig {
884
+ /**
885
+ * List of tool names to exclude from the default agent.
886
+ * These tools remain available to custom sub-agents that reference them in their `tools` array.
887
+ * Use this to register tools that should only be accessed via delegation to sub-agents,
888
+ * keeping the default agent's context clean.
889
+ */
890
+ excludedTools?: string[];
833
891
  }
834
892
  /**
835
893
  * Configuration for infinite sessions with automatic context compaction and workspace persistence.
@@ -956,6 +1014,16 @@ export interface SessionConfig {
956
1014
  */
957
1015
  workingDirectory?: string;
958
1016
  streaming?: boolean;
1017
+ /**
1018
+ * Include sub-agent streaming events in the event stream. When true, streaming
1019
+ * delta events from sub-agents (e.g., `assistant.message_delta`,
1020
+ * `assistant.reasoning_delta`, `assistant.streaming_delta` with `agentId` set)
1021
+ * are forwarded to this connection. When false, only non-streaming sub-agent
1022
+ * events and `subagent.*` lifecycle events are forwarded; streaming deltas from
1023
+ * sub-agents are suppressed.
1024
+ * @default true
1025
+ */
1026
+ includeSubAgentStreamingEvents?: boolean;
959
1027
  /**
960
1028
  * MCP server configurations for the session.
961
1029
  * Keys are server names, values are server configurations.
@@ -965,6 +1033,13 @@ export interface SessionConfig {
965
1033
  * Custom agent configurations for the session.
966
1034
  */
967
1035
  customAgents?: CustomAgentConfig[];
1036
+ /**
1037
+ * Configuration for the default agent (the built-in agent that handles
1038
+ * turns when no custom agent is selected).
1039
+ * Use `excludedTools` to hide specific tools from the default agent while keeping
1040
+ * them available to custom sub-agents.
1041
+ */
1042
+ defaultAgent?: DefaultAgentConfig;
968
1043
  /**
969
1044
  * Name of the custom agent to activate when the session starts.
970
1045
  * Must match the `name` of one of the agents in `customAgents`.
@@ -999,12 +1074,12 @@ export interface SessionConfig {
999
1074
  * Supplies a handler for session filesystem operations. This takes effect
1000
1075
  * only if {@link CopilotClientOptions.sessionFs} is configured.
1001
1076
  */
1002
- createSessionFsHandler?: (session: CopilotSession) => SessionFsHandler;
1077
+ createSessionFsHandler?: (session: CopilotSession) => SessionFsProvider;
1003
1078
  }
1004
1079
  /**
1005
1080
  * Configuration for resuming a session
1006
1081
  */
1007
- export type ResumeSessionConfig = Pick<SessionConfig, "clientName" | "model" | "tools" | "commands" | "systemMessage" | "availableTools" | "excludedTools" | "provider" | "modelCapabilities" | "streaming" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "onElicitationRequest" | "hooks" | "workingDirectory" | "configDir" | "enableConfigDiscovery" | "mcpServers" | "customAgents" | "agent" | "skillDirectories" | "disabledSkills" | "infiniteSessions" | "onEvent" | "createSessionFsHandler"> & {
1082
+ export type ResumeSessionConfig = Pick<SessionConfig, "clientName" | "model" | "tools" | "commands" | "systemMessage" | "availableTools" | "excludedTools" | "provider" | "modelCapabilities" | "streaming" | "includeSubAgentStreamingEvents" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "onElicitationRequest" | "hooks" | "workingDirectory" | "configDir" | "enableConfigDiscovery" | "mcpServers" | "customAgents" | "defaultAgent" | "agent" | "skillDirectories" | "disabledSkills" | "infiniteSessions" | "onEvent" | "createSessionFsHandler"> & {
1008
1083
  /**
1009
1084
  * When true, skips emitting the session.resume event.
1010
1085
  * Useful for reconnecting to a session without triggering resume-related side effects.
@@ -1047,6 +1122,10 @@ export interface ProviderConfig {
1047
1122
  */
1048
1123
  apiVersion?: string;
1049
1124
  };
1125
+ /**
1126
+ * Custom HTTP headers to include in outbound provider requests.
1127
+ */
1128
+ headers?: Record<string, string>;
1050
1129
  }
1051
1130
  /**
1052
1131
  * Options for sending a message to a session
@@ -1094,6 +1173,10 @@ export interface MessageOptions {
1094
1173
  * - "immediate": Send immediately
1095
1174
  */
1096
1175
  mode?: "enqueue" | "immediate";
1176
+ /**
1177
+ * Custom HTTP headers to include in outbound model requests for this turn.
1178
+ */
1179
+ requestHeaders?: Record<string, string>;
1097
1180
  }
1098
1181
  /**
1099
1182
  * All possible event type strings from SessionEvent
package/dist/types.js CHANGED
@@ -1,3 +1,45 @@
1
+ import { createSessionFsAdapter } from "./sessionFsProvider.js";
2
+ function convertMcpCallToolResult(callResult) {
3
+ const textParts = [];
4
+ const binaryResults = [];
5
+ for (const block of callResult.content) {
6
+ switch (block.type) {
7
+ case "text":
8
+ if (typeof block.text === "string") {
9
+ textParts.push(block.text);
10
+ }
11
+ break;
12
+ case "image":
13
+ if (typeof block.data === "string" && block.data && typeof block.mimeType === "string") {
14
+ binaryResults.push({
15
+ data: block.data,
16
+ mimeType: block.mimeType,
17
+ type: "image"
18
+ });
19
+ }
20
+ break;
21
+ case "resource": {
22
+ if (block.resource?.text) {
23
+ textParts.push(block.resource.text);
24
+ }
25
+ if (block.resource?.blob) {
26
+ binaryResults.push({
27
+ data: block.resource.blob,
28
+ mimeType: block.resource.mimeType ?? "application/octet-stream",
29
+ type: "resource",
30
+ description: block.resource.uri
31
+ });
32
+ }
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ return {
38
+ textResultForLlm: textParts.join("\n"),
39
+ resultType: callResult.isError ? "failure" : "success",
40
+ ...binaryResults.length > 0 ? { binaryResultsForLlm: binaryResults } : {}
41
+ };
42
+ }
1
43
  function defineTool(name, config) {
2
44
  return { name, ...config };
3
45
  }
@@ -16,8 +58,14 @@ const SYSTEM_PROMPT_SECTIONS = {
16
58
  }
17
59
  };
18
60
  const approveAll = () => ({ kind: "approved" });
61
+ const defaultJoinSessionPermissionHandler = () => ({
62
+ kind: "no-result"
63
+ });
19
64
  export {
20
65
  SYSTEM_PROMPT_SECTIONS,
21
66
  approveAll,
67
+ convertMcpCallToolResult,
68
+ createSessionFsAdapter,
69
+ defaultJoinSessionPermissionHandler,
22
70
  defineTool
23
71
  };
@@ -18,6 +18,7 @@ For user-scoped extensions (persist across all repos), add `location: "user"`.
18
18
  ### Step 2: Edit the extension file
19
19
 
20
20
  Modify the generated `extension.mjs` using `edit` or `create` tools. The file must:
21
+
21
22
  - Be named `extension.mjs` (only `.mjs` is supported)
22
23
  - Use ES module syntax (`import`/`export`)
23
24
  - Call `joinSession({ ... })`
@@ -48,6 +49,7 @@ Check that the extension loaded successfully and isn't marked as "failed".
48
49
  ```
49
50
 
50
51
  Discovery rules:
52
+
51
53
  - The CLI scans `.github/extensions/` relative to the git root
52
54
  - It also scans the user's copilot config extensions directory
53
55
  - Only immediate subdirectories are checked (not recursive)
@@ -62,8 +64,8 @@ Discovery rules:
62
64
  import { joinSession } from "@github/copilot-sdk/extension";
63
65
 
64
66
  await joinSession({
65
- tools: [], // Optional — custom tools
66
- hooks: {}, // Optional — lifecycle hooks
67
+ tools: [], // Optional — custom tools
68
+ hooks: {}, // Optional — lifecycle hooks
67
69
  });
68
70
  ```
69
71
 
@@ -74,9 +76,10 @@ await joinSession({
74
76
  ```js
75
77
  tools: [
76
78
  {
77
- name: "tool_name", // Required. Must be globally unique across all extensions.
79
+ name: "tool_name", // Required. Must be globally unique across all extensions.
78
80
  description: "What it does", // Required. Shown to the agent in tool descriptions.
79
- parameters: { // Optional. JSON Schema for the arguments.
81
+ parameters: {
82
+ // Optional. JSON Schema for the arguments.
80
83
  type: "object",
81
84
  properties: {
82
85
  arg1: { type: "string", description: "..." },
@@ -96,10 +99,11 @@ tools: [
96
99
  return `Result: ${args.arg1}`;
97
100
  },
98
101
  },
99
- ]
102
+ ];
100
103
  ```
101
104
 
102
105
  **Constraints:**
106
+
103
107
  - Tool names must be unique across ALL loaded extensions. Collisions cause the second extension to fail to load.
104
108
  - Handler must return a string or `{ textResultForLlm: string, resultType?: string }`.
105
109
  - Handler receives `(args, invocation)` — the second argument has `sessionId`, `toolCallId`, `toolName`.
@@ -195,6 +199,7 @@ After `joinSession()`, the returned `session` provides:
195
199
  ### session.send(options)
196
200
 
197
201
  Send a message programmatically:
202
+
198
203
  ```js
199
204
  await session.send({ prompt: "Analyze the test results." });
200
205
  await session.send({
@@ -206,6 +211,7 @@ await session.send({
206
211
  ### session.sendAndWait(options, timeout?)
207
212
 
208
213
  Send and block until the agent finishes (resolves on `session.idle`):
214
+
209
215
  ```js
210
216
  const response = await session.sendAndWait({ prompt: "What is 2+2?" });
211
217
  // response?.data.content contains the agent's reply
@@ -214,6 +220,7 @@ const response = await session.sendAndWait({ prompt: "What is 2+2?" });
214
220
  ### session.log(message, options?)
215
221
 
216
222
  Log to the CLI timeline:
223
+
217
224
  ```js
218
225
  await session.log("Extension ready");
219
226
  await session.log("Rate limit approaching", { level: "warning" });
@@ -224,6 +231,7 @@ await session.log("Processing...", { ephemeral: true }); // transient, not persi
224
231
  ### session.on(eventType, handler)
225
232
 
226
233
  Subscribe to session events. Returns an unsubscribe function.
234
+
227
235
  ```js
228
236
  const unsub = session.on("tool.execution_complete", (event) => {
229
237
  // event.data.toolName, event.data.success, event.data.result
@@ -232,16 +240,16 @@ const unsub = session.on("tool.execution_complete", (event) => {
232
240
 
233
241
  ### Key Event Types
234
242
 
235
- | Event | Key Data Fields |
236
- |-------|----------------|
237
- | `assistant.message` | `content`, `messageId` |
238
- | `tool.execution_start` | `toolCallId`, `toolName`, `arguments` |
243
+ | Event | Key Data Fields |
244
+ | ------------------------- | ------------------------------------------------------ |
245
+ | `assistant.message` | `content`, `messageId` |
246
+ | `tool.execution_start` | `toolCallId`, `toolName`, `arguments` |
239
247
  | `tool.execution_complete` | `toolCallId`, `toolName`, `success`, `result`, `error` |
240
- | `user.message` | `content`, `attachments`, `source` |
241
- | `session.idle` | `backgroundTasks` |
242
- | `session.error` | `errorType`, `message`, `stack` |
243
- | `permission.requested` | `requestId`, `permissionRequest.kind` |
244
- | `session.shutdown` | `shutdownType`, `totalPremiumRequests` |
248
+ | `user.message` | `content`, `attachments`, `source` |
249
+ | `session.idle` | `backgroundTasks` |
250
+ | `session.error` | `errorType`, `message`, `stack` |
251
+ | `permission.requested` | `requestId`, `permissionRequest.kind` |
252
+ | `session.shutdown` | `shutdownType`, `totalPremiumRequests` |
245
253
 
246
254
  ### session.workspacePath
247
255