@github/copilot-sdk 1.0.0-beta.1 → 1.0.0-beta.10
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 +52 -43
- package/dist/canvas.d.ts +126 -0
- package/dist/canvas.js +49 -0
- package/dist/cjs/canvas.js +75 -0
- package/dist/cjs/client.js +536 -299
- package/dist/cjs/extension.js +9 -2
- package/dist/cjs/generated/rpc.js +1319 -14
- package/dist/cjs/index.js +22 -7
- package/dist/cjs/session.js +179 -93
- package/dist/cjs/sessionFsProvider.js +34 -0
- package/dist/cjs/toolSet.js +107 -0
- package/dist/cjs/types.js +38 -4
- package/dist/client.d.ts +63 -73
- package/dist/client.js +537 -300
- package/dist/extension.d.ts +3 -1
- package/dist/extension.js +10 -2
- package/dist/generated/rpc.d.ts +10187 -1028
- package/dist/generated/rpc.js +1319 -14
- package/dist/generated/session-events.d.ts +1982 -195
- package/dist/index.d.ts +6 -2
- package/dist/index.js +15 -2
- package/dist/session.d.ts +18 -186
- package/dist/session.js +179 -92
- package/dist/sessionFsProvider.d.ts +29 -2
- package/dist/sessionFsProvider.js +34 -0
- package/dist/toolSet.d.ts +75 -0
- package/dist/toolSet.js +82 -0
- package/dist/types.d.ts +730 -123
- package/dist/types.js +36 -3
- package/docs/agent-author.md +31 -7
- package/docs/examples.md +23 -16
- package/package.json +3 -3
package/dist/session.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { ConnectionError, ResponseError } from "vscode-jsonrpc/node.js";
|
|
1
|
+
import { ConnectionError, ErrorCodes, ResponseError } from "vscode-jsonrpc/node.js";
|
|
2
2
|
import { createSessionRpc } from "./generated/rpc.js";
|
|
3
|
+
import { CanvasError } from "./canvas.js";
|
|
3
4
|
import { getTraceContext } from "./telemetry.js";
|
|
4
|
-
|
|
5
|
+
function deserializeHookInput(raw) {
|
|
6
|
+
if (!raw || typeof raw !== "object" || typeof raw.timestamp !== "number") {
|
|
7
|
+
return raw;
|
|
8
|
+
}
|
|
9
|
+
const obj = raw;
|
|
10
|
+
const { cwd, ...rest } = obj;
|
|
11
|
+
return { ...rest, timestamp: new Date(obj.timestamp), workingDirectory: cwd };
|
|
12
|
+
}
|
|
13
|
+
function isOpenCanvasInstance(value) {
|
|
14
|
+
if (!value || typeof value !== "object") {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const instance = value;
|
|
18
|
+
return typeof instance.instanceId === "string" && instance.instanceId.length > 0 && typeof instance.extensionId === "string" && instance.extensionId.length > 0 && typeof instance.canvasId === "string" && instance.canvasId.length > 0 && typeof instance.reopen === "boolean" && (instance.availability === "ready" || instance.availability === "stale");
|
|
19
|
+
}
|
|
5
20
|
class CopilotSession {
|
|
6
21
|
/**
|
|
7
22
|
* Creates a new CopilotSession instance.
|
|
@@ -21,15 +36,19 @@ class CopilotSession {
|
|
|
21
36
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
22
37
|
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
23
38
|
toolHandlers = /* @__PURE__ */ new Map();
|
|
39
|
+
canvases = /* @__PURE__ */ new Map();
|
|
24
40
|
commandHandlers = /* @__PURE__ */ new Map();
|
|
25
41
|
permissionHandler;
|
|
26
42
|
userInputHandler;
|
|
27
43
|
elicitationHandler;
|
|
44
|
+
exitPlanModeHandler;
|
|
45
|
+
autoModeSwitchHandler;
|
|
28
46
|
hooks;
|
|
29
47
|
transformCallbacks;
|
|
30
48
|
_rpc = null;
|
|
31
49
|
traceContextProvider;
|
|
32
50
|
_capabilities = {};
|
|
51
|
+
openCanvasInstances = [];
|
|
33
52
|
/** @internal Client session API handlers, populated by CopilotClient during create/resume. */
|
|
34
53
|
clientSessionApis = {};
|
|
35
54
|
/**
|
|
@@ -76,59 +95,22 @@ class CopilotSession {
|
|
|
76
95
|
input: (message, options) => this._input(message, options)
|
|
77
96
|
};
|
|
78
97
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
* The message is processed asynchronously. Subscribe to events via {@link on}
|
|
83
|
-
* to receive streaming responses and other session events.
|
|
84
|
-
*
|
|
85
|
-
* @param options - The message options including the prompt and optional attachments
|
|
86
|
-
* @returns A promise that resolves with the message ID of the response
|
|
87
|
-
* @throws Error if the session has been disconnected or the connection fails
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```typescript
|
|
91
|
-
* const messageId = await session.send({
|
|
92
|
-
* prompt: "Explain this code",
|
|
93
|
-
* attachments: [{ type: "file", path: "./src/index.ts" }]
|
|
94
|
-
* });
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
async send(options) {
|
|
98
|
+
async send(optionsOrPrompt) {
|
|
99
|
+
const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
|
|
98
100
|
const response = await this.connection.sendRequest("session.send", {
|
|
99
101
|
...await getTraceContext(this.traceContextProvider),
|
|
100
102
|
sessionId: this.sessionId,
|
|
101
103
|
prompt: options.prompt,
|
|
104
|
+
displayPrompt: options.displayPrompt,
|
|
102
105
|
attachments: options.attachments,
|
|
103
106
|
mode: options.mode,
|
|
107
|
+
agentMode: options.agentMode,
|
|
104
108
|
requestHeaders: options.requestHeaders
|
|
105
109
|
});
|
|
106
110
|
return response.messageId;
|
|
107
111
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*
|
|
111
|
-
* This is a convenience method that combines {@link send} with waiting for
|
|
112
|
-
* the `session.idle` event. Use this when you want to block until the
|
|
113
|
-
* assistant has finished processing the message.
|
|
114
|
-
*
|
|
115
|
-
* Events are still delivered to handlers registered via {@link on} while waiting.
|
|
116
|
-
*
|
|
117
|
-
* @param options - The message options including the prompt and optional attachments
|
|
118
|
-
* @param timeout - Timeout in milliseconds (default: 60000). Controls how long to wait; does not abort in-flight agent work.
|
|
119
|
-
* @returns A promise that resolves with the final assistant message when the session becomes idle,
|
|
120
|
-
* or undefined if no assistant message was received
|
|
121
|
-
* @throws Error if the timeout is reached before the session becomes idle
|
|
122
|
-
* @throws Error if the session has been disconnected or the connection fails
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```typescript
|
|
126
|
-
* // Send and wait for completion with default 60s timeout
|
|
127
|
-
* const response = await session.sendAndWait({ prompt: "What is 2+2?" });
|
|
128
|
-
* console.log(response?.data.content); // "4"
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
async sendAndWait(options, timeout) {
|
|
112
|
+
async sendAndWait(optionsOrPrompt, timeout) {
|
|
113
|
+
const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
|
|
132
114
|
const effectiveTimeout = timeout ?? 6e4;
|
|
133
115
|
let resolveIdle;
|
|
134
116
|
let rejectWithError;
|
|
@@ -269,6 +251,25 @@ class CopilotSession {
|
|
|
269
251
|
}
|
|
270
252
|
} else if (event.type === "capabilities.changed") {
|
|
271
253
|
this._capabilities = { ...this._capabilities, ...event.data };
|
|
254
|
+
} else if (event.type === "session.canvas.opened") {
|
|
255
|
+
this.upsertOpenCanvasFromEvent(event.data);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
upsertOpenCanvasFromEvent(data) {
|
|
259
|
+
if (!isOpenCanvasInstance(data)) {
|
|
260
|
+
console.warn("failed to deserialize session.canvas.opened payload");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.upsertOpenCanvas(data);
|
|
264
|
+
}
|
|
265
|
+
upsertOpenCanvas(instance) {
|
|
266
|
+
const index = this.openCanvasInstances.findIndex(
|
|
267
|
+
(open) => open.instanceId === instance.instanceId
|
|
268
|
+
);
|
|
269
|
+
if (index >= 0) {
|
|
270
|
+
this.openCanvasInstances[index] = instance;
|
|
271
|
+
} else {
|
|
272
|
+
this.openCanvasInstances.push(instance);
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
/**
|
|
@@ -371,8 +372,8 @@ class CopilotSession {
|
|
|
371
372
|
/**
|
|
372
373
|
* Registers custom tool handlers for this session.
|
|
373
374
|
*
|
|
374
|
-
* Tools allow the assistant to execute custom functions.
|
|
375
|
-
*
|
|
375
|
+
* Tools with handlers allow the assistant to execute custom functions automatically.
|
|
376
|
+
* Declaration-only tools are surfaced as events and left pending for the consumer.
|
|
376
377
|
*
|
|
377
378
|
* @param tools - An array of tool definitions with their handlers, or undefined to clear all tools
|
|
378
379
|
* @internal This method is typically called internally when creating a session with tools.
|
|
@@ -383,7 +384,9 @@ class CopilotSession {
|
|
|
383
384
|
return;
|
|
384
385
|
}
|
|
385
386
|
for (const tool of tools) {
|
|
386
|
-
|
|
387
|
+
if (tool.handler) {
|
|
388
|
+
this.toolHandlers.set(tool.name, tool.handler);
|
|
389
|
+
}
|
|
387
390
|
}
|
|
388
391
|
}
|
|
389
392
|
/**
|
|
@@ -396,6 +399,61 @@ class CopilotSession {
|
|
|
396
399
|
getToolHandler(name) {
|
|
397
400
|
return this.toolHandlers.get(name);
|
|
398
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Registers canvas declarations and handlers for this session.
|
|
404
|
+
*
|
|
405
|
+
* @param canvases - Canvases created via `createCanvas`, or undefined to clear all canvases
|
|
406
|
+
* @internal Called by the SDK when creating/resuming a session with `canvases`.
|
|
407
|
+
*/
|
|
408
|
+
registerCanvases(canvases) {
|
|
409
|
+
this.canvases.clear();
|
|
410
|
+
if (!canvases || canvases.length === 0) {
|
|
411
|
+
delete this.clientSessionApis.canvas;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
for (const canvas of canvases) {
|
|
415
|
+
this.canvases.set(canvas.declaration.id, canvas);
|
|
416
|
+
}
|
|
417
|
+
const self = this;
|
|
418
|
+
this.clientSessionApis.canvas = {
|
|
419
|
+
async open(params) {
|
|
420
|
+
const canvas = self.canvases.get(params.canvasId);
|
|
421
|
+
if (!canvas) throw new Error(`No canvas registered with id "${params.canvasId}"`);
|
|
422
|
+
try {
|
|
423
|
+
return await canvas.open(params) ?? {};
|
|
424
|
+
} catch (error) {
|
|
425
|
+
throw toCanvasRpcError(error);
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
async close(params) {
|
|
429
|
+
const canvas = self.canvases.get(params.canvasId);
|
|
430
|
+
if (!canvas) throw new Error(`No canvas registered with id "${params.canvasId}"`);
|
|
431
|
+
try {
|
|
432
|
+
if (canvas.onClose) {
|
|
433
|
+
await canvas.onClose(params);
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
throw toCanvasRpcError(error);
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
async invoke(params) {
|
|
440
|
+
const canvas = self.canvases.get(params.canvasId);
|
|
441
|
+
if (!canvas) throw new Error(`No canvas registered with id "${params.canvasId}"`);
|
|
442
|
+
const handler = canvas.actionHandlers.get(params.actionName);
|
|
443
|
+
if (!handler) {
|
|
444
|
+
throw new CanvasError(
|
|
445
|
+
"canvas_action_no_handler",
|
|
446
|
+
"No handler implemented for this canvas action"
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
return await handler(params);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
throw toCanvasRpcError(error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
399
457
|
/**
|
|
400
458
|
* Registers command handlers for this session.
|
|
401
459
|
*
|
|
@@ -420,6 +478,24 @@ class CopilotSession {
|
|
|
420
478
|
registerElicitationHandler(handler) {
|
|
421
479
|
this.elicitationHandler = handler;
|
|
422
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Registers the exit-plan-mode handler for this session.
|
|
483
|
+
*
|
|
484
|
+
* @param handler - The handler to invoke when the server dispatches an exit-plan-mode request
|
|
485
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
486
|
+
*/
|
|
487
|
+
registerExitPlanModeHandler(handler) {
|
|
488
|
+
this.exitPlanModeHandler = handler;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Registers the auto-mode-switch handler for this session.
|
|
492
|
+
*
|
|
493
|
+
* @param handler - The handler to invoke when the server dispatches an auto-mode-switch request
|
|
494
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
495
|
+
*/
|
|
496
|
+
registerAutoModeSwitchHandler(handler) {
|
|
497
|
+
this.autoModeSwitchHandler = handler;
|
|
498
|
+
}
|
|
423
499
|
/**
|
|
424
500
|
* Handles an elicitation.requested broadcast event.
|
|
425
501
|
* Invokes the registered handler and responds via handlePendingElicitation RPC.
|
|
@@ -445,6 +521,26 @@ class CopilotSession {
|
|
|
445
521
|
}
|
|
446
522
|
}
|
|
447
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Handles an exitPlanMode.request callback from the runtime.
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
async _handleExitPlanModeRequest(request) {
|
|
529
|
+
if (!this.exitPlanModeHandler) {
|
|
530
|
+
return { approved: true };
|
|
531
|
+
}
|
|
532
|
+
return await this.exitPlanModeHandler(request, { sessionId: this.sessionId });
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Handles an autoModeSwitch.request callback from the runtime.
|
|
536
|
+
* @internal
|
|
537
|
+
*/
|
|
538
|
+
async _handleAutoModeSwitchRequest(request) {
|
|
539
|
+
if (!this.autoModeSwitchHandler) {
|
|
540
|
+
return "no";
|
|
541
|
+
}
|
|
542
|
+
return await this.autoModeSwitchHandler(request, { sessionId: this.sessionId });
|
|
543
|
+
}
|
|
448
544
|
/**
|
|
449
545
|
* Sets the host capabilities for this session.
|
|
450
546
|
*
|
|
@@ -454,6 +550,24 @@ class CopilotSession {
|
|
|
454
550
|
setCapabilities(capabilities) {
|
|
455
551
|
this._capabilities = capabilities ?? {};
|
|
456
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Snapshot of canvas instances currently known to be open for this session.
|
|
555
|
+
* Populated from the `session.resume` response and live `session.canvas.opened`
|
|
556
|
+
* events. Returns a defensive copy — mutating the returned array has no effect
|
|
557
|
+
* on the session.
|
|
558
|
+
*/
|
|
559
|
+
get openCanvases() {
|
|
560
|
+
return [...this.openCanvasInstances];
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Sets the open-canvas snapshot for this session.
|
|
564
|
+
*
|
|
565
|
+
* @param instances - The `openCanvases` array from the `session.resume` response.
|
|
566
|
+
* @internal This method is typically called internally when resuming a session.
|
|
567
|
+
*/
|
|
568
|
+
setOpenCanvases(instances) {
|
|
569
|
+
this.openCanvasInstances = [...instances];
|
|
570
|
+
}
|
|
457
571
|
assertElicitation() {
|
|
458
572
|
if (!this._capabilities.ui?.elicitation) {
|
|
459
573
|
throw new Error(
|
|
@@ -593,33 +707,6 @@ class CopilotSession {
|
|
|
593
707
|
}
|
|
594
708
|
return { sections: result };
|
|
595
709
|
}
|
|
596
|
-
/**
|
|
597
|
-
* Handles a permission request in the v2 protocol format (synchronous RPC).
|
|
598
|
-
* Used as a back-compat adapter when connected to a v2 server.
|
|
599
|
-
*
|
|
600
|
-
* @param request - The permission request data from the CLI
|
|
601
|
-
* @returns A promise that resolves with the permission decision
|
|
602
|
-
* @internal This method is for internal use by the SDK.
|
|
603
|
-
*/
|
|
604
|
-
async _handlePermissionRequestV2(request) {
|
|
605
|
-
if (!this.permissionHandler) {
|
|
606
|
-
return { kind: "user-not-available" };
|
|
607
|
-
}
|
|
608
|
-
try {
|
|
609
|
-
const result = await this.permissionHandler(request, {
|
|
610
|
-
sessionId: this.sessionId
|
|
611
|
-
});
|
|
612
|
-
if (result.kind === "no-result") {
|
|
613
|
-
throw new Error(NO_RESULT_PERMISSION_V2_ERROR);
|
|
614
|
-
}
|
|
615
|
-
return result;
|
|
616
|
-
} catch (error) {
|
|
617
|
-
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
618
|
-
throw error;
|
|
619
|
-
}
|
|
620
|
-
return { kind: "user-not-available" };
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
710
|
/**
|
|
624
711
|
* Handles a user input request from the Copilot CLI.
|
|
625
712
|
*
|
|
@@ -652,9 +739,12 @@ class CopilotSession {
|
|
|
652
739
|
if (!this.hooks) {
|
|
653
740
|
return void 0;
|
|
654
741
|
}
|
|
742
|
+
const normalized = deserializeHookInput(input);
|
|
655
743
|
const handlerMap = {
|
|
656
744
|
preToolUse: this.hooks.onPreToolUse,
|
|
745
|
+
preMcpToolCall: this.hooks.onPreMcpToolCall,
|
|
657
746
|
postToolUse: this.hooks.onPostToolUse,
|
|
747
|
+
postToolUseFailure: this.hooks.onPostToolUseFailure,
|
|
658
748
|
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
|
|
659
749
|
sessionStart: this.hooks.onSessionStart,
|
|
660
750
|
sessionEnd: this.hooks.onSessionEnd,
|
|
@@ -665,7 +755,7 @@ class CopilotSession {
|
|
|
665
755
|
return void 0;
|
|
666
756
|
}
|
|
667
757
|
try {
|
|
668
|
-
const result = await handler(
|
|
758
|
+
const result = await handler(normalized, { sessionId: this.sessionId });
|
|
669
759
|
return result;
|
|
670
760
|
} catch (_error) {
|
|
671
761
|
return void 0;
|
|
@@ -682,7 +772,7 @@ class CopilotSession {
|
|
|
682
772
|
*
|
|
683
773
|
* @example
|
|
684
774
|
* ```typescript
|
|
685
|
-
* const events = await session.
|
|
775
|
+
* const events = await session.getEvents();
|
|
686
776
|
* for (const event of events) {
|
|
687
777
|
* if (event.type === "assistant.message") {
|
|
688
778
|
* console.log("Assistant:", event.data.content);
|
|
@@ -690,7 +780,7 @@ class CopilotSession {
|
|
|
690
780
|
* }
|
|
691
781
|
* ```
|
|
692
782
|
*/
|
|
693
|
-
async
|
|
783
|
+
async getEvents() {
|
|
694
784
|
const response = await this.connection.sendRequest("session.getMessages", {
|
|
695
785
|
sessionId: this.sessionId
|
|
696
786
|
});
|
|
@@ -725,18 +815,10 @@ class CopilotSession {
|
|
|
725
815
|
this.typedEventHandlers.clear();
|
|
726
816
|
this.toolHandlers.clear();
|
|
727
817
|
this.permissionHandler = void 0;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
* Disconnects this session and releases all in-memory resources.
|
|
733
|
-
* Session data on disk is preserved for later resumption.
|
|
734
|
-
*
|
|
735
|
-
* @returns A promise that resolves when the session is disconnected
|
|
736
|
-
* @throws Error if the connection fails
|
|
737
|
-
*/
|
|
738
|
-
async destroy() {
|
|
739
|
-
return this.disconnect();
|
|
818
|
+
this.userInputHandler = void 0;
|
|
819
|
+
this.elicitationHandler = void 0;
|
|
820
|
+
this.exitPlanModeHandler = void 0;
|
|
821
|
+
this.autoModeSwitchHandler = void 0;
|
|
740
822
|
}
|
|
741
823
|
/** Enables `await using session = ...` syntax for automatic cleanup. */
|
|
742
824
|
async [Symbol.asyncDispose]() {
|
|
@@ -822,7 +904,12 @@ function isToolResultObject(value) {
|
|
|
822
904
|
];
|
|
823
905
|
return allowedResultTypes.includes(value.resultType);
|
|
824
906
|
}
|
|
907
|
+
function toCanvasRpcError(error) {
|
|
908
|
+
if (error instanceof ResponseError) return error;
|
|
909
|
+
const code = error instanceof CanvasError ? error.code : "canvas_handler_error";
|
|
910
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
911
|
+
return new ResponseError(ErrorCodes.InternalError, message, { code, message });
|
|
912
|
+
}
|
|
825
913
|
export {
|
|
826
|
-
CopilotSession
|
|
827
|
-
NO_RESULT_PERMISSION_V2_ERROR
|
|
914
|
+
CopilotSession
|
|
828
915
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SessionFsHandler, SessionFsStatResult, SessionFsReaddirWithTypesEntry } from "./generated/rpc.js";
|
|
1
|
+
import type { SessionFsHandler, SessionFsStatResult, SessionFsReaddirWithTypesEntry, SessionFsSqliteQueryResult as GeneratedSqliteQueryResult, SessionFsSqliteQueryType } from "./generated/rpc.js";
|
|
2
|
+
export type { SessionFsSqliteQueryType };
|
|
2
3
|
/**
|
|
3
4
|
* File metadata returned by {@link SessionFsProvider.stat}.
|
|
4
5
|
* Same shape as the generated {@link SessionFsStatResult} but without the
|
|
@@ -6,7 +7,31 @@ import type { SessionFsHandler, SessionFsStatResult, SessionFsReaddirWithTypesEn
|
|
|
6
7
|
*/
|
|
7
8
|
export type SessionFsFileInfo = Omit<SessionFsStatResult, "error">;
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
+
* Result of a SQLite query execution via {@link SessionFsSqliteProvider.query}.
|
|
11
|
+
* Same shape as the generated {@link GeneratedSqliteQueryResult} but without the
|
|
12
|
+
* `error` field, since providers signal errors by throwing.
|
|
13
|
+
*/
|
|
14
|
+
export type SessionFsSqliteQueryResult = Omit<GeneratedSqliteQueryResult, "error">;
|
|
15
|
+
/**
|
|
16
|
+
* SQLite operations for the per-session database.
|
|
17
|
+
* Implementers provide query execution and existence checking.
|
|
18
|
+
*/
|
|
19
|
+
export interface SessionFsSqliteProvider {
|
|
20
|
+
/**
|
|
21
|
+
* Execute a SQLite query against the per-session database.
|
|
22
|
+
*
|
|
23
|
+
* @param queryType - How to execute: `"exec"` for DDL/multi-statement, `"query"` for SELECT, `"run"` for INSERT/UPDATE/DELETE.
|
|
24
|
+
* @param query - SQL query to execute.
|
|
25
|
+
* @param params - Optional named bind parameters.
|
|
26
|
+
*/
|
|
27
|
+
query(queryType: SessionFsSqliteQueryType, query: string, params?: Record<string, string | number | null>): Promise<SessionFsSqliteQueryResult | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Check whether the per-session database already exists, without creating it.
|
|
30
|
+
*/
|
|
31
|
+
exists(): Promise<boolean>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Interface for session filesystem providers. Implementers use idiomatic
|
|
10
35
|
* TypeScript patterns: throw on error, return values directly. Use
|
|
11
36
|
* {@link createSessionFsAdapter} to convert a provider into the
|
|
12
37
|
* {@link SessionFsHandler} expected by the SDK.
|
|
@@ -35,6 +60,8 @@ export interface SessionFsProvider {
|
|
|
35
60
|
rm(path: string, recursive: boolean, force: boolean): Promise<void>;
|
|
36
61
|
/** Renames/moves a file or directory. */
|
|
37
62
|
rename(src: string, dest: string): Promise<void>;
|
|
63
|
+
/** Per-session SQLite database operations. Optional — omit if the provider does not support SQLite. */
|
|
64
|
+
sqlite?: SessionFsSqliteProvider;
|
|
38
65
|
}
|
|
39
66
|
/**
|
|
40
67
|
* Wraps a {@link SessionFsProvider} into the {@link SessionFsHandler}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
function normalizeSqliteParams(params) {
|
|
2
|
+
if (!params) {
|
|
3
|
+
return void 0;
|
|
4
|
+
}
|
|
5
|
+
const normalized = {};
|
|
6
|
+
for (const [key, value] of Object.entries(params)) {
|
|
7
|
+
if (value !== void 0) {
|
|
8
|
+
normalized[key] = value;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return normalized;
|
|
12
|
+
}
|
|
1
13
|
function createSessionFsAdapter(provider) {
|
|
2
14
|
return {
|
|
3
15
|
readFile: async ({ path }) => {
|
|
@@ -84,6 +96,28 @@ function createSessionFsAdapter(provider) {
|
|
|
84
96
|
} catch (err) {
|
|
85
97
|
return toSessionFsError(err);
|
|
86
98
|
}
|
|
99
|
+
},
|
|
100
|
+
// Unlike the FS methods above, SQLite methods let errors propagate to the JSON-RPC layer
|
|
101
|
+
// rather than catching and mapping via toSessionFsError. The FS error mapping is specifically
|
|
102
|
+
// for translating Node.js errno codes (e.g., ENOENT) into SessionFsError, which isn't
|
|
103
|
+
// meaningful for SQL errors. Letting exceptions propagate preserves the original error
|
|
104
|
+
// message in the JSON-RPC error response.
|
|
105
|
+
sqliteQuery: async ({ queryType, query, params: bindParams }) => {
|
|
106
|
+
if (!provider.sqlite) {
|
|
107
|
+
throw new Error("SQLite is not supported by this provider");
|
|
108
|
+
}
|
|
109
|
+
const result = await provider.sqlite.query(
|
|
110
|
+
queryType,
|
|
111
|
+
query,
|
|
112
|
+
normalizeSqliteParams(bindParams)
|
|
113
|
+
);
|
|
114
|
+
return result ?? { rows: [], columns: [], rowsAffected: 0 };
|
|
115
|
+
},
|
|
116
|
+
sqliteExists: async () => {
|
|
117
|
+
if (!provider.sqlite) {
|
|
118
|
+
throw new Error("SQLite is not supported by this provider");
|
|
119
|
+
}
|
|
120
|
+
return { exists: await provider.sqlite.exists() };
|
|
87
121
|
}
|
|
88
122
|
};
|
|
89
123
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder that produces a list of source-qualified tool filter strings for
|
|
3
|
+
* {@link SessionConfigBase.availableTools}.
|
|
4
|
+
*
|
|
5
|
+
* Tools are classified by the runtime at registration time (not from name
|
|
6
|
+
* parsing), so `addBuiltIn("foo")` matches only tools the runtime registered
|
|
7
|
+
* as built-in, even if an MCP server or custom-agent extension happens to
|
|
8
|
+
* register a tool with the same wire name.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const tools = new ToolSet()
|
|
13
|
+
* .addBuiltIn(BuiltInTools.Isolated)
|
|
14
|
+
* .addMcp("*")
|
|
15
|
+
* .addCustom("*");
|
|
16
|
+
*
|
|
17
|
+
* const session = await client.createSession({
|
|
18
|
+
* availableTools: tools,
|
|
19
|
+
* // ...
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class ToolSet {
|
|
24
|
+
private readonly items;
|
|
25
|
+
/**
|
|
26
|
+
* Adds one or more built-in tool patterns.
|
|
27
|
+
*
|
|
28
|
+
* @param name A specific built-in tool name (e.g. `"bash"`) or `"*"` to match all
|
|
29
|
+
* built-in tools.
|
|
30
|
+
*/
|
|
31
|
+
addBuiltIn(name: string): ToolSet;
|
|
32
|
+
/**
|
|
33
|
+
* Adds a list of built-in tool patterns (e.g. {@link BuiltInTools.Isolated}).
|
|
34
|
+
*/
|
|
35
|
+
addBuiltIn(names: readonly string[]): ToolSet;
|
|
36
|
+
/**
|
|
37
|
+
* Adds a custom tool pattern. Matches tools registered via the SDK's
|
|
38
|
+
* `tools` option or via custom agents.
|
|
39
|
+
*
|
|
40
|
+
* @param name A specific custom tool name or `"*"` to match all custom tools.
|
|
41
|
+
*/
|
|
42
|
+
addCustom(name: string): ToolSet;
|
|
43
|
+
/**
|
|
44
|
+
* Adds an MCP tool pattern. Matches tools advertised by any configured
|
|
45
|
+
* MCP server.
|
|
46
|
+
*
|
|
47
|
+
* @param toolName The runtime's canonical wire name for the MCP tool
|
|
48
|
+
* (e.g. `"github-list_issues"`), or `"*"` to match all MCP tools from
|
|
49
|
+
* any server.
|
|
50
|
+
*/
|
|
51
|
+
addMcp(toolName: string): ToolSet;
|
|
52
|
+
/**
|
|
53
|
+
* Returns a defensive copy of the accumulated filter strings, suitable for
|
|
54
|
+
* passing as {@link SessionConfigBase.availableTools}.
|
|
55
|
+
*/
|
|
56
|
+
toArray(): string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Curated sets of built-in tool names for common scenarios. Each constant is
|
|
60
|
+
* meant to be passed to {@link ToolSet.addBuiltIn}.
|
|
61
|
+
*/
|
|
62
|
+
export declare const BuiltInTools: {
|
|
63
|
+
/**
|
|
64
|
+
* Built-in tools that operate only within the bounds of a single session —
|
|
65
|
+
* no host filesystem access outside the session, no cross-session state,
|
|
66
|
+
* no host environment access, no network. Safe to enable in `Mode = "empty"`
|
|
67
|
+
* scenarios (e.g. multi-tenant servers) without leaking host capabilities.
|
|
68
|
+
*
|
|
69
|
+
* **Contract:** tools in this set MUST NOT be extended (even behind options
|
|
70
|
+
* or args) to read or write state outside the session boundary. Adding
|
|
71
|
+
* cross-session or host-state behavior to one of these tools is a
|
|
72
|
+
* breaking change that requires removing it from this set.
|
|
73
|
+
*/
|
|
74
|
+
readonly Isolated: readonly string[];
|
|
75
|
+
};
|
package/dist/toolSet.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const VALID_TOOL_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
2
|
+
function validateName(kind, name) {
|
|
3
|
+
if (name === "*") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (!VALID_TOOL_NAME.test(name)) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`Invalid ${kind} tool name '${name}': tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard '*'.`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
class ToolSet {
|
|
13
|
+
items = [];
|
|
14
|
+
addBuiltIn(nameOrNames) {
|
|
15
|
+
const names = typeof nameOrNames === "string" ? [nameOrNames] : nameOrNames;
|
|
16
|
+
for (const name of names) {
|
|
17
|
+
validateName("builtin", name);
|
|
18
|
+
this.items.push(`builtin:${name}`);
|
|
19
|
+
}
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Adds a custom tool pattern. Matches tools registered via the SDK's
|
|
24
|
+
* `tools` option or via custom agents.
|
|
25
|
+
*
|
|
26
|
+
* @param name A specific custom tool name or `"*"` to match all custom tools.
|
|
27
|
+
*/
|
|
28
|
+
addCustom(name) {
|
|
29
|
+
validateName("custom", name);
|
|
30
|
+
this.items.push(`custom:${name}`);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Adds an MCP tool pattern. Matches tools advertised by any configured
|
|
35
|
+
* MCP server.
|
|
36
|
+
*
|
|
37
|
+
* @param toolName The runtime's canonical wire name for the MCP tool
|
|
38
|
+
* (e.g. `"github-list_issues"`), or `"*"` to match all MCP tools from
|
|
39
|
+
* any server.
|
|
40
|
+
*/
|
|
41
|
+
addMcp(toolName) {
|
|
42
|
+
validateName("mcp", toolName);
|
|
43
|
+
this.items.push(`mcp:${toolName}`);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns a defensive copy of the accumulated filter strings, suitable for
|
|
48
|
+
* passing as {@link SessionConfigBase.availableTools}.
|
|
49
|
+
*/
|
|
50
|
+
toArray() {
|
|
51
|
+
return [...this.items];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const BuiltInTools = {
|
|
55
|
+
/**
|
|
56
|
+
* Built-in tools that operate only within the bounds of a single session —
|
|
57
|
+
* no host filesystem access outside the session, no cross-session state,
|
|
58
|
+
* no host environment access, no network. Safe to enable in `Mode = "empty"`
|
|
59
|
+
* scenarios (e.g. multi-tenant servers) without leaking host capabilities.
|
|
60
|
+
*
|
|
61
|
+
* **Contract:** tools in this set MUST NOT be extended (even behind options
|
|
62
|
+
* or args) to read or write state outside the session boundary. Adding
|
|
63
|
+
* cross-session or host-state behavior to one of these tools is a
|
|
64
|
+
* breaking change that requires removing it from this set.
|
|
65
|
+
*/
|
|
66
|
+
Isolated: [
|
|
67
|
+
"ask_user",
|
|
68
|
+
"task_complete",
|
|
69
|
+
"exit_plan_mode",
|
|
70
|
+
"task",
|
|
71
|
+
"read_agent",
|
|
72
|
+
"write_agent",
|
|
73
|
+
"list_agents",
|
|
74
|
+
"send_inbox",
|
|
75
|
+
"context_board",
|
|
76
|
+
"skill"
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
export {
|
|
80
|
+
BuiltInTools,
|
|
81
|
+
ToolSet
|
|
82
|
+
};
|