@github/copilot-sdk 0.1.33-unstable.1 → 0.2.1-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/README.md +276 -6
- package/dist/cjs/client.js +1329 -0
- package/dist/cjs/extension.js +45 -0
- package/dist/cjs/generated/rpc.js +123 -0
- package/dist/cjs/generated/session-events.js +16 -0
- package/dist/cjs/index.js +38 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/sdkProtocolVersion.js +33 -0
- package/dist/cjs/session.js +774 -0
- package/dist/cjs/telemetry.js +35 -0
- package/dist/cjs/types.js +49 -0
- package/dist/client.d.ts +2 -4
- package/dist/client.js +216 -88
- package/dist/extension.d.ts +6 -7
- package/dist/extension.js +5 -1
- package/dist/generated/rpc.d.ts +540 -1
- package/dist/generated/rpc.js +41 -2
- package/dist/generated/session-events.d.ts +829 -25
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/session.d.ts +81 -4
- package/dist/session.js +231 -8
- package/dist/telemetry.d.ts +14 -0
- package/dist/telemetry.js +11 -0
- package/dist/types.d.ts +340 -6
- package/dist/types.js +15 -0
- package/docs/agent-author.md +0 -2
- package/docs/examples.md +2 -15
- package/docs/extensions.md +0 -2
- package/package.json +19 -7
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var session_exports = {};
|
|
20
|
+
__export(session_exports, {
|
|
21
|
+
CopilotSession: () => CopilotSession,
|
|
22
|
+
NO_RESULT_PERMISSION_V2_ERROR: () => NO_RESULT_PERMISSION_V2_ERROR
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(session_exports);
|
|
25
|
+
var import_node = require("vscode-jsonrpc/node.js");
|
|
26
|
+
var import_rpc = require("./generated/rpc.js");
|
|
27
|
+
var import_telemetry = require("./telemetry.js");
|
|
28
|
+
const NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
|
|
29
|
+
class CopilotSession {
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new CopilotSession instance.
|
|
32
|
+
*
|
|
33
|
+
* @param sessionId - The unique identifier for this session
|
|
34
|
+
* @param connection - The JSON-RPC message connection to the Copilot CLI
|
|
35
|
+
* @param workspacePath - Path to the session workspace directory (when infinite sessions enabled)
|
|
36
|
+
* @param traceContextProvider - Optional callback to get W3C Trace Context for outbound RPCs
|
|
37
|
+
* @internal This constructor is internal. Use {@link CopilotClient.createSession} to create sessions.
|
|
38
|
+
*/
|
|
39
|
+
constructor(sessionId, connection, _workspacePath, traceContextProvider) {
|
|
40
|
+
this.sessionId = sessionId;
|
|
41
|
+
this.connection = connection;
|
|
42
|
+
this._workspacePath = _workspacePath;
|
|
43
|
+
this.traceContextProvider = traceContextProvider;
|
|
44
|
+
}
|
|
45
|
+
eventHandlers = /* @__PURE__ */ new Set();
|
|
46
|
+
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
47
|
+
toolHandlers = /* @__PURE__ */ new Map();
|
|
48
|
+
commandHandlers = /* @__PURE__ */ new Map();
|
|
49
|
+
permissionHandler;
|
|
50
|
+
userInputHandler;
|
|
51
|
+
hooks;
|
|
52
|
+
transformCallbacks;
|
|
53
|
+
_rpc = null;
|
|
54
|
+
traceContextProvider;
|
|
55
|
+
_capabilities = {};
|
|
56
|
+
/**
|
|
57
|
+
* Typed session-scoped RPC methods.
|
|
58
|
+
*/
|
|
59
|
+
get rpc() {
|
|
60
|
+
if (!this._rpc) {
|
|
61
|
+
this._rpc = (0, import_rpc.createSessionRpc)(this.connection, this.sessionId);
|
|
62
|
+
}
|
|
63
|
+
return this._rpc;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Path to the session workspace directory when infinite sessions are enabled.
|
|
67
|
+
* Contains checkpoints/, plan.md, and files/ subdirectories.
|
|
68
|
+
* Undefined if infinite sessions are disabled.
|
|
69
|
+
*/
|
|
70
|
+
get workspacePath() {
|
|
71
|
+
return this._workspacePath;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Host capabilities reported when the session was created or resumed.
|
|
75
|
+
* Use this to check feature support before calling capability-gated APIs.
|
|
76
|
+
*/
|
|
77
|
+
get capabilities() {
|
|
78
|
+
return this._capabilities;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Interactive UI methods for showing dialogs to the user.
|
|
82
|
+
* Only available when the CLI host supports elicitation
|
|
83
|
+
* (`session.capabilities.ui?.elicitation === true`).
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* if (session.capabilities.ui?.elicitation) {
|
|
88
|
+
* const ok = await session.ui.confirm("Deploy to production?");
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
get ui() {
|
|
93
|
+
return {
|
|
94
|
+
elicitation: (params) => this._elicitation(params),
|
|
95
|
+
confirm: (message) => this._confirm(message),
|
|
96
|
+
select: (message, options) => this._select(message, options),
|
|
97
|
+
input: (message, options) => this._input(message, options)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Sends a message to this session and waits for the response.
|
|
102
|
+
*
|
|
103
|
+
* The message is processed asynchronously. Subscribe to events via {@link on}
|
|
104
|
+
* to receive streaming responses and other session events.
|
|
105
|
+
*
|
|
106
|
+
* @param options - The message options including the prompt and optional attachments
|
|
107
|
+
* @returns A promise that resolves with the message ID of the response
|
|
108
|
+
* @throws Error if the session has been disconnected or the connection fails
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const messageId = await session.send({
|
|
113
|
+
* prompt: "Explain this code",
|
|
114
|
+
* attachments: [{ type: "file", path: "./src/index.ts" }]
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
async send(options) {
|
|
119
|
+
const response = await this.connection.sendRequest("session.send", {
|
|
120
|
+
...await (0, import_telemetry.getTraceContext)(this.traceContextProvider),
|
|
121
|
+
sessionId: this.sessionId,
|
|
122
|
+
prompt: options.prompt,
|
|
123
|
+
attachments: options.attachments,
|
|
124
|
+
mode: options.mode
|
|
125
|
+
});
|
|
126
|
+
return response.messageId;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Sends a message to this session and waits until the session becomes idle.
|
|
130
|
+
*
|
|
131
|
+
* This is a convenience method that combines {@link send} with waiting for
|
|
132
|
+
* the `session.idle` event. Use this when you want to block until the
|
|
133
|
+
* assistant has finished processing the message.
|
|
134
|
+
*
|
|
135
|
+
* Events are still delivered to handlers registered via {@link on} while waiting.
|
|
136
|
+
*
|
|
137
|
+
* @param options - The message options including the prompt and optional attachments
|
|
138
|
+
* @param timeout - Timeout in milliseconds (default: 60000). Controls how long to wait; does not abort in-flight agent work.
|
|
139
|
+
* @returns A promise that resolves with the final assistant message when the session becomes idle,
|
|
140
|
+
* or undefined if no assistant message was received
|
|
141
|
+
* @throws Error if the timeout is reached before the session becomes idle
|
|
142
|
+
* @throws Error if the session has been disconnected or the connection fails
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // Send and wait for completion with default 60s timeout
|
|
147
|
+
* const response = await session.sendAndWait({ prompt: "What is 2+2?" });
|
|
148
|
+
* console.log(response?.data.content); // "4"
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
async sendAndWait(options, timeout) {
|
|
152
|
+
const effectiveTimeout = timeout ?? 6e4;
|
|
153
|
+
let resolveIdle;
|
|
154
|
+
let rejectWithError;
|
|
155
|
+
const idlePromise = new Promise((resolve, reject) => {
|
|
156
|
+
resolveIdle = resolve;
|
|
157
|
+
rejectWithError = reject;
|
|
158
|
+
});
|
|
159
|
+
let lastAssistantMessage;
|
|
160
|
+
const unsubscribe = this.on((event) => {
|
|
161
|
+
if (event.type === "assistant.message") {
|
|
162
|
+
lastAssistantMessage = event;
|
|
163
|
+
} else if (event.type === "session.idle") {
|
|
164
|
+
resolveIdle();
|
|
165
|
+
} else if (event.type === "session.error") {
|
|
166
|
+
const error = new Error(event.data.message);
|
|
167
|
+
error.stack = event.data.stack;
|
|
168
|
+
rejectWithError(error);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
let timeoutId;
|
|
172
|
+
try {
|
|
173
|
+
await this.send(options);
|
|
174
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
175
|
+
timeoutId = setTimeout(
|
|
176
|
+
() => reject(
|
|
177
|
+
new Error(
|
|
178
|
+
`Timeout after ${effectiveTimeout}ms waiting for session.idle`
|
|
179
|
+
)
|
|
180
|
+
),
|
|
181
|
+
effectiveTimeout
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
await Promise.race([idlePromise, timeoutPromise]);
|
|
185
|
+
return lastAssistantMessage;
|
|
186
|
+
} finally {
|
|
187
|
+
if (timeoutId !== void 0) {
|
|
188
|
+
clearTimeout(timeoutId);
|
|
189
|
+
}
|
|
190
|
+
unsubscribe();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
on(eventTypeOrHandler, handler) {
|
|
194
|
+
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
195
|
+
const eventType = eventTypeOrHandler;
|
|
196
|
+
if (!this.typedEventHandlers.has(eventType)) {
|
|
197
|
+
this.typedEventHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
198
|
+
}
|
|
199
|
+
const storedHandler = handler;
|
|
200
|
+
this.typedEventHandlers.get(eventType).add(storedHandler);
|
|
201
|
+
return () => {
|
|
202
|
+
const handlers = this.typedEventHandlers.get(eventType);
|
|
203
|
+
if (handlers) {
|
|
204
|
+
handlers.delete(storedHandler);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const wildcardHandler = eventTypeOrHandler;
|
|
209
|
+
this.eventHandlers.add(wildcardHandler);
|
|
210
|
+
return () => {
|
|
211
|
+
this.eventHandlers.delete(wildcardHandler);
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Dispatches an event to all registered handlers.
|
|
216
|
+
* Also handles broadcast request events internally (external tool calls, permissions).
|
|
217
|
+
*
|
|
218
|
+
* @param event - The session event to dispatch
|
|
219
|
+
* @internal This method is for internal use by the SDK.
|
|
220
|
+
*/
|
|
221
|
+
_dispatchEvent(event) {
|
|
222
|
+
this._handleBroadcastEvent(event);
|
|
223
|
+
const typedHandlers = this.typedEventHandlers.get(event.type);
|
|
224
|
+
if (typedHandlers) {
|
|
225
|
+
for (const handler of typedHandlers) {
|
|
226
|
+
try {
|
|
227
|
+
handler(event);
|
|
228
|
+
} catch (_error) {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const handler of this.eventHandlers) {
|
|
233
|
+
try {
|
|
234
|
+
handler(event);
|
|
235
|
+
} catch (_error) {
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Handles broadcast request events by executing local handlers and responding via RPC.
|
|
241
|
+
* Handlers are dispatched as fire-and-forget — rejections propagate as unhandled promise
|
|
242
|
+
* rejections, consistent with standard EventEmitter / event handler semantics.
|
|
243
|
+
* @internal
|
|
244
|
+
*/
|
|
245
|
+
_handleBroadcastEvent(event) {
|
|
246
|
+
if (event.type === "external_tool.requested") {
|
|
247
|
+
const { requestId, toolName } = event.data;
|
|
248
|
+
const args = event.data.arguments;
|
|
249
|
+
const toolCallId = event.data.toolCallId;
|
|
250
|
+
const traceparent = event.data.traceparent;
|
|
251
|
+
const tracestate = event.data.tracestate;
|
|
252
|
+
const handler = this.toolHandlers.get(toolName);
|
|
253
|
+
if (handler) {
|
|
254
|
+
void this._executeToolAndRespond(
|
|
255
|
+
requestId,
|
|
256
|
+
toolName,
|
|
257
|
+
toolCallId,
|
|
258
|
+
args,
|
|
259
|
+
handler,
|
|
260
|
+
traceparent,
|
|
261
|
+
tracestate
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
} else if (event.type === "permission.requested") {
|
|
265
|
+
const { requestId, permissionRequest } = event.data;
|
|
266
|
+
if (this.permissionHandler) {
|
|
267
|
+
void this._executePermissionAndRespond(requestId, permissionRequest);
|
|
268
|
+
}
|
|
269
|
+
} else if (event.type === "command.execute") {
|
|
270
|
+
const { requestId, commandName, command, args } = event.data;
|
|
271
|
+
void this._executeCommandAndRespond(requestId, commandName, command, args);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Executes a tool handler and sends the result back via RPC.
|
|
276
|
+
* @internal
|
|
277
|
+
*/
|
|
278
|
+
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
|
|
279
|
+
try {
|
|
280
|
+
const rawResult = await handler(args, {
|
|
281
|
+
sessionId: this.sessionId,
|
|
282
|
+
toolCallId,
|
|
283
|
+
toolName,
|
|
284
|
+
arguments: args,
|
|
285
|
+
traceparent,
|
|
286
|
+
tracestate
|
|
287
|
+
});
|
|
288
|
+
let result;
|
|
289
|
+
if (rawResult == null) {
|
|
290
|
+
result = "";
|
|
291
|
+
} else if (typeof rawResult === "string") {
|
|
292
|
+
result = rawResult;
|
|
293
|
+
} else {
|
|
294
|
+
result = JSON.stringify(rawResult);
|
|
295
|
+
}
|
|
296
|
+
await this.rpc.tools.handlePendingToolCall({ requestId, result });
|
|
297
|
+
} catch (error) {
|
|
298
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
299
|
+
try {
|
|
300
|
+
await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
|
|
301
|
+
} catch (rpcError) {
|
|
302
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
303
|
+
throw rpcError;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Executes a permission handler and sends the result back via RPC.
|
|
310
|
+
* @internal
|
|
311
|
+
*/
|
|
312
|
+
async _executePermissionAndRespond(requestId, permissionRequest) {
|
|
313
|
+
try {
|
|
314
|
+
const result = await this.permissionHandler(permissionRequest, {
|
|
315
|
+
sessionId: this.sessionId
|
|
316
|
+
});
|
|
317
|
+
if (result.kind === "no-result") {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result });
|
|
321
|
+
} catch (_error) {
|
|
322
|
+
try {
|
|
323
|
+
await this.rpc.permissions.handlePendingPermissionRequest({
|
|
324
|
+
requestId,
|
|
325
|
+
result: {
|
|
326
|
+
kind: "denied-no-approval-rule-and-could-not-request-from-user"
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
} catch (rpcError) {
|
|
330
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
331
|
+
throw rpcError;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Executes a command handler and sends the result back via RPC.
|
|
338
|
+
* @internal
|
|
339
|
+
*/
|
|
340
|
+
async _executeCommandAndRespond(requestId, commandName, command, args) {
|
|
341
|
+
const handler = this.commandHandlers.get(commandName);
|
|
342
|
+
if (!handler) {
|
|
343
|
+
try {
|
|
344
|
+
await this.rpc.commands.handlePendingCommand({
|
|
345
|
+
requestId,
|
|
346
|
+
error: `Unknown command: ${commandName}`
|
|
347
|
+
});
|
|
348
|
+
} catch (rpcError) {
|
|
349
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
350
|
+
throw rpcError;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
await handler({ sessionId: this.sessionId, command, commandName, args });
|
|
357
|
+
await this.rpc.commands.handlePendingCommand({ requestId });
|
|
358
|
+
} catch (error) {
|
|
359
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
360
|
+
try {
|
|
361
|
+
await this.rpc.commands.handlePendingCommand({ requestId, error: message });
|
|
362
|
+
} catch (rpcError) {
|
|
363
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
364
|
+
throw rpcError;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Registers custom tool handlers for this session.
|
|
371
|
+
*
|
|
372
|
+
* Tools allow the assistant to execute custom functions. When the assistant
|
|
373
|
+
* invokes a tool, the corresponding handler is called with the tool arguments.
|
|
374
|
+
*
|
|
375
|
+
* @param tools - An array of tool definitions with their handlers, or undefined to clear all tools
|
|
376
|
+
* @internal This method is typically called internally when creating a session with tools.
|
|
377
|
+
*/
|
|
378
|
+
registerTools(tools) {
|
|
379
|
+
this.toolHandlers.clear();
|
|
380
|
+
if (!tools) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
for (const tool of tools) {
|
|
384
|
+
this.toolHandlers.set(tool.name, tool.handler);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Retrieves a registered tool handler by name.
|
|
389
|
+
*
|
|
390
|
+
* @param name - The name of the tool to retrieve
|
|
391
|
+
* @returns The tool handler if found, or undefined
|
|
392
|
+
* @internal This method is for internal use by the SDK.
|
|
393
|
+
*/
|
|
394
|
+
getToolHandler(name) {
|
|
395
|
+
return this.toolHandlers.get(name);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Registers command handlers for this session.
|
|
399
|
+
*
|
|
400
|
+
* @param commands - An array of command definitions with handlers, or undefined to clear
|
|
401
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
402
|
+
*/
|
|
403
|
+
registerCommands(commands) {
|
|
404
|
+
this.commandHandlers.clear();
|
|
405
|
+
if (!commands) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
for (const cmd of commands) {
|
|
409
|
+
this.commandHandlers.set(cmd.name, cmd.handler);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Sets the host capabilities for this session.
|
|
414
|
+
*
|
|
415
|
+
* @param capabilities - The capabilities object from the create/resume response
|
|
416
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
417
|
+
*/
|
|
418
|
+
setCapabilities(capabilities) {
|
|
419
|
+
this._capabilities = capabilities ?? {};
|
|
420
|
+
}
|
|
421
|
+
assertElicitation() {
|
|
422
|
+
if (!this._capabilities.ui?.elicitation) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
"Elicitation is not supported by the host. Check session.capabilities.ui?.elicitation before calling UI methods."
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async _elicitation(params) {
|
|
429
|
+
this.assertElicitation();
|
|
430
|
+
return this.rpc.ui.elicitation({
|
|
431
|
+
message: params.message,
|
|
432
|
+
requestedSchema: params.requestedSchema
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
async _confirm(message) {
|
|
436
|
+
this.assertElicitation();
|
|
437
|
+
const result = await this.rpc.ui.elicitation({
|
|
438
|
+
message,
|
|
439
|
+
requestedSchema: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
confirmed: { type: "boolean", default: true }
|
|
443
|
+
},
|
|
444
|
+
required: ["confirmed"]
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
return result.action === "accept" && result.content?.confirmed === true;
|
|
448
|
+
}
|
|
449
|
+
async _select(message, options) {
|
|
450
|
+
this.assertElicitation();
|
|
451
|
+
const result = await this.rpc.ui.elicitation({
|
|
452
|
+
message,
|
|
453
|
+
requestedSchema: {
|
|
454
|
+
type: "object",
|
|
455
|
+
properties: {
|
|
456
|
+
selection: { type: "string", enum: options }
|
|
457
|
+
},
|
|
458
|
+
required: ["selection"]
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
if (result.action === "accept" && result.content?.selection != null) {
|
|
462
|
+
return result.content.selection;
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
async _input(message, options) {
|
|
467
|
+
this.assertElicitation();
|
|
468
|
+
const field = { type: "string" };
|
|
469
|
+
if (options?.title) field.title = options.title;
|
|
470
|
+
if (options?.description) field.description = options.description;
|
|
471
|
+
if (options?.minLength != null) field.minLength = options.minLength;
|
|
472
|
+
if (options?.maxLength != null) field.maxLength = options.maxLength;
|
|
473
|
+
if (options?.format) field.format = options.format;
|
|
474
|
+
if (options?.default != null) field.default = options.default;
|
|
475
|
+
const result = await this.rpc.ui.elicitation({
|
|
476
|
+
message,
|
|
477
|
+
requestedSchema: {
|
|
478
|
+
type: "object",
|
|
479
|
+
properties: {
|
|
480
|
+
value: field
|
|
481
|
+
},
|
|
482
|
+
required: ["value"]
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
if (result.action === "accept" && result.content?.value != null) {
|
|
486
|
+
return result.content.value;
|
|
487
|
+
}
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Registers a handler for permission requests.
|
|
492
|
+
*
|
|
493
|
+
* When the assistant needs permission to perform certain actions (e.g., file operations),
|
|
494
|
+
* this handler is called to approve or deny the request.
|
|
495
|
+
*
|
|
496
|
+
* @param handler - The permission handler function, or undefined to remove the handler
|
|
497
|
+
* @internal This method is typically called internally when creating a session.
|
|
498
|
+
*/
|
|
499
|
+
registerPermissionHandler(handler) {
|
|
500
|
+
this.permissionHandler = handler;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Registers a user input handler for ask_user requests.
|
|
504
|
+
*
|
|
505
|
+
* When the agent needs input from the user (via ask_user tool),
|
|
506
|
+
* this handler is called to provide the response.
|
|
507
|
+
*
|
|
508
|
+
* @param handler - The user input handler function, or undefined to remove the handler
|
|
509
|
+
* @internal This method is typically called internally when creating a session.
|
|
510
|
+
*/
|
|
511
|
+
registerUserInputHandler(handler) {
|
|
512
|
+
this.userInputHandler = handler;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Registers hook handlers for session lifecycle events.
|
|
516
|
+
*
|
|
517
|
+
* Hooks allow custom logic to be executed at various points during
|
|
518
|
+
* the session lifecycle (before/after tool use, session start/end, etc.).
|
|
519
|
+
*
|
|
520
|
+
* @param hooks - The hook handlers object, or undefined to remove all hooks
|
|
521
|
+
* @internal This method is typically called internally when creating a session.
|
|
522
|
+
*/
|
|
523
|
+
registerHooks(hooks) {
|
|
524
|
+
this.hooks = hooks;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Registers transform callbacks for system message sections.
|
|
528
|
+
*
|
|
529
|
+
* @param callbacks - Map of section ID to transform callback, or undefined to clear
|
|
530
|
+
* @internal This method is typically called internally when creating a session.
|
|
531
|
+
*/
|
|
532
|
+
registerTransformCallbacks(callbacks) {
|
|
533
|
+
this.transformCallbacks = callbacks;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Handles a systemMessage.transform request from the runtime.
|
|
537
|
+
* Dispatches each section to its registered transform callback.
|
|
538
|
+
*
|
|
539
|
+
* @param sections - Map of section IDs to their current rendered content
|
|
540
|
+
* @returns A promise that resolves with the transformed sections
|
|
541
|
+
* @internal This method is for internal use by the SDK.
|
|
542
|
+
*/
|
|
543
|
+
async _handleSystemMessageTransform(sections) {
|
|
544
|
+
const result = {};
|
|
545
|
+
for (const [sectionId, { content }] of Object.entries(sections)) {
|
|
546
|
+
const callback = this.transformCallbacks?.get(sectionId);
|
|
547
|
+
if (callback) {
|
|
548
|
+
try {
|
|
549
|
+
const transformed = await callback(content);
|
|
550
|
+
result[sectionId] = { content: transformed };
|
|
551
|
+
} catch (_error) {
|
|
552
|
+
result[sectionId] = { content };
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
result[sectionId] = { content };
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return { sections: result };
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Handles a permission request in the v2 protocol format (synchronous RPC).
|
|
562
|
+
* Used as a back-compat adapter when connected to a v2 server.
|
|
563
|
+
*
|
|
564
|
+
* @param request - The permission request data from the CLI
|
|
565
|
+
* @returns A promise that resolves with the permission decision
|
|
566
|
+
* @internal This method is for internal use by the SDK.
|
|
567
|
+
*/
|
|
568
|
+
async _handlePermissionRequestV2(request) {
|
|
569
|
+
if (!this.permissionHandler) {
|
|
570
|
+
return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
const result = await this.permissionHandler(request, {
|
|
574
|
+
sessionId: this.sessionId
|
|
575
|
+
});
|
|
576
|
+
if (result.kind === "no-result") {
|
|
577
|
+
throw new Error(NO_RESULT_PERMISSION_V2_ERROR);
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
580
|
+
} catch (error) {
|
|
581
|
+
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Handles a user input request from the Copilot CLI.
|
|
589
|
+
*
|
|
590
|
+
* @param request - The user input request data from the CLI
|
|
591
|
+
* @returns A promise that resolves with the user's response
|
|
592
|
+
* @internal This method is for internal use by the SDK.
|
|
593
|
+
*/
|
|
594
|
+
async _handleUserInputRequest(request) {
|
|
595
|
+
if (!this.userInputHandler) {
|
|
596
|
+
throw new Error("User input requested but no handler registered");
|
|
597
|
+
}
|
|
598
|
+
try {
|
|
599
|
+
const result = await this.userInputHandler(request, {
|
|
600
|
+
sessionId: this.sessionId
|
|
601
|
+
});
|
|
602
|
+
return result;
|
|
603
|
+
} catch (error) {
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Handles a hooks invocation from the Copilot CLI.
|
|
609
|
+
*
|
|
610
|
+
* @param hookType - The type of hook being invoked
|
|
611
|
+
* @param input - The input data for the hook
|
|
612
|
+
* @returns A promise that resolves with the hook output, or undefined
|
|
613
|
+
* @internal This method is for internal use by the SDK.
|
|
614
|
+
*/
|
|
615
|
+
async _handleHooksInvoke(hookType, input) {
|
|
616
|
+
if (!this.hooks) {
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
const handlerMap = {
|
|
620
|
+
preToolUse: this.hooks.onPreToolUse,
|
|
621
|
+
postToolUse: this.hooks.onPostToolUse,
|
|
622
|
+
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
|
|
623
|
+
sessionStart: this.hooks.onSessionStart,
|
|
624
|
+
sessionEnd: this.hooks.onSessionEnd,
|
|
625
|
+
errorOccurred: this.hooks.onErrorOccurred
|
|
626
|
+
};
|
|
627
|
+
const handler = handlerMap[hookType];
|
|
628
|
+
if (!handler) {
|
|
629
|
+
return void 0;
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
const result = await handler(input, { sessionId: this.sessionId });
|
|
633
|
+
return result;
|
|
634
|
+
} catch (_error) {
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Retrieves all events and messages from this session's history.
|
|
640
|
+
*
|
|
641
|
+
* This returns the complete conversation history including user messages,
|
|
642
|
+
* assistant responses, tool executions, and other session events.
|
|
643
|
+
*
|
|
644
|
+
* @returns A promise that resolves with an array of all session events
|
|
645
|
+
* @throws Error if the session has been disconnected or the connection fails
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```typescript
|
|
649
|
+
* const events = await session.getMessages();
|
|
650
|
+
* for (const event of events) {
|
|
651
|
+
* if (event.type === "assistant.message") {
|
|
652
|
+
* console.log("Assistant:", event.data.content);
|
|
653
|
+
* }
|
|
654
|
+
* }
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
async getMessages() {
|
|
658
|
+
const response = await this.connection.sendRequest("session.getMessages", {
|
|
659
|
+
sessionId: this.sessionId
|
|
660
|
+
});
|
|
661
|
+
return response.events;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Disconnects this session and releases all in-memory resources (event handlers,
|
|
665
|
+
* tool handlers, permission handlers).
|
|
666
|
+
*
|
|
667
|
+
* Session state on disk (conversation history, planning state, artifacts) is
|
|
668
|
+
* preserved, so the conversation can be resumed later by calling
|
|
669
|
+
* {@link CopilotClient.resumeSession} with the session ID. To permanently
|
|
670
|
+
* remove all session data including files on disk, use
|
|
671
|
+
* {@link CopilotClient.deleteSession} instead.
|
|
672
|
+
*
|
|
673
|
+
* After calling this method, the session object can no longer be used.
|
|
674
|
+
*
|
|
675
|
+
* @returns A promise that resolves when the session is disconnected
|
|
676
|
+
* @throws Error if the connection fails
|
|
677
|
+
*
|
|
678
|
+
* @example
|
|
679
|
+
* ```typescript
|
|
680
|
+
* // Clean up when done — session can still be resumed later
|
|
681
|
+
* await session.disconnect();
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
684
|
+
async disconnect() {
|
|
685
|
+
await this.connection.sendRequest("session.destroy", {
|
|
686
|
+
sessionId: this.sessionId
|
|
687
|
+
});
|
|
688
|
+
this.eventHandlers.clear();
|
|
689
|
+
this.typedEventHandlers.clear();
|
|
690
|
+
this.toolHandlers.clear();
|
|
691
|
+
this.permissionHandler = void 0;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* @deprecated Use {@link disconnect} instead. This method will be removed in a future release.
|
|
695
|
+
*
|
|
696
|
+
* Disconnects this session and releases all in-memory resources.
|
|
697
|
+
* Session data on disk is preserved for later resumption.
|
|
698
|
+
*
|
|
699
|
+
* @returns A promise that resolves when the session is disconnected
|
|
700
|
+
* @throws Error if the connection fails
|
|
701
|
+
*/
|
|
702
|
+
async destroy() {
|
|
703
|
+
return this.disconnect();
|
|
704
|
+
}
|
|
705
|
+
/** Enables `await using session = ...` syntax for automatic cleanup. */
|
|
706
|
+
async [Symbol.asyncDispose]() {
|
|
707
|
+
return this.disconnect();
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Aborts the currently processing message in this session.
|
|
711
|
+
*
|
|
712
|
+
* Use this to cancel a long-running request. The session remains valid
|
|
713
|
+
* and can continue to be used for new messages.
|
|
714
|
+
*
|
|
715
|
+
* @returns A promise that resolves when the abort request is acknowledged
|
|
716
|
+
* @throws Error if the session has been disconnected or the connection fails
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```typescript
|
|
720
|
+
* // Start a long-running request
|
|
721
|
+
* const messagePromise = session.send({ prompt: "Write a very long story..." });
|
|
722
|
+
*
|
|
723
|
+
* // Abort after 5 seconds
|
|
724
|
+
* setTimeout(async () => {
|
|
725
|
+
* await session.abort();
|
|
726
|
+
* }, 5000);
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
async abort() {
|
|
730
|
+
await this.connection.sendRequest("session.abort", {
|
|
731
|
+
sessionId: this.sessionId
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Change the model for this session.
|
|
736
|
+
* The new model takes effect for the next message. Conversation history is preserved.
|
|
737
|
+
*
|
|
738
|
+
* @param model - Model ID to switch to
|
|
739
|
+
* @param options - Optional settings for the new model
|
|
740
|
+
*
|
|
741
|
+
* @example
|
|
742
|
+
* ```typescript
|
|
743
|
+
* await session.setModel("gpt-4.1");
|
|
744
|
+
* await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
async setModel(model, options) {
|
|
748
|
+
await this.rpc.model.switchTo({ modelId: model, ...options });
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Log a message to the session timeline.
|
|
752
|
+
* The message appears in the session event stream and is visible to SDK consumers
|
|
753
|
+
* and (for non-ephemeral messages) persisted to the session event log on disk.
|
|
754
|
+
*
|
|
755
|
+
* @param message - Human-readable message text
|
|
756
|
+
* @param options - Optional log level and ephemeral flag
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* await session.log("Processing started");
|
|
761
|
+
* await session.log("Disk usage high", { level: "warning" });
|
|
762
|
+
* await session.log("Connection failed", { level: "error" });
|
|
763
|
+
* await session.log("Debug info", { ephemeral: true });
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
async log(message, options) {
|
|
767
|
+
await this.rpc.log({ message, ...options });
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
771
|
+
0 && (module.exports = {
|
|
772
|
+
CopilotSession,
|
|
773
|
+
NO_RESULT_PERMISSION_V2_ERROR
|
|
774
|
+
});
|