@github/copilot-sdk 1.0.0-beta.1 → 1.0.0-beta.11

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/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
- const NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
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
- * Sends a message to this session and waits for the response.
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
- * Sends a message to this session and waits until the session becomes idle.
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. When the assistant
375
- * invokes a tool, the corresponding handler is called with the tool arguments.
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
- this.toolHandlers.set(tool.name, tool.handler);
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(input, { sessionId: this.sessionId });
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.getMessages();
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 getMessages() {
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
- * @deprecated Use {@link disconnect} instead. This method will be removed in a future release.
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
- * Interface for session filesystem providers. Implementors use idiomatic
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
+ };
@@ -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
+ };