@github/copilot-sdk 1.0.0-beta.7 → 1.0.0-beta.9

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 CHANGED
@@ -957,7 +957,7 @@ const session = await client.createSession({
957
957
  };
958
958
  },
959
959
 
960
- // Called after each tool execution
960
+ // Called after each successful tool execution
961
961
  onPostToolUse: async (input, invocation) => {
962
962
  console.log(`Tool ${input.toolName} completed`);
963
963
  // Optionally modify the result or add context
@@ -966,6 +966,16 @@ const session = await client.createSession({
966
966
  };
967
967
  },
968
968
 
969
+ // Called after a tool execution whose result was "failure".
970
+ // onPostToolUse does NOT fire for failed tool calls — register this
971
+ // hook to observe them. Input includes `error` (the failure message
972
+ // extracted from the tool's result), not the full result object.
973
+ onPostToolUseFailure: async (input, invocation) => {
974
+ console.log(`Tool ${input.toolName} failed: ${input.error}`);
975
+ // Optionally append hidden guidance to the model.
976
+ return { additionalContext: "Suggest checking inputs and retrying." };
977
+ },
978
+
969
979
  // Called when user submits a prompt
970
980
  onUserPromptSubmitted: async (input, invocation) => {
971
981
  console.log(`User prompt: ${input.prompt}`);
@@ -1001,7 +1011,8 @@ const session = await client.createSession({
1001
1011
  **Available hooks:**
1002
1012
 
1003
1013
  - `onPreToolUse` - Intercept tool calls before execution. Can allow/deny or modify arguments.
1004
- - `onPostToolUse` - Process tool results after execution. Can modify results or add context.
1014
+ - `onPostToolUse` - Process tool results after **successful** execution. Can modify results or add context.
1015
+ - `onPostToolUseFailure` - Observe and append hidden guidance to the model after tool executions whose result was `"failure"`. Register this in addition to `onPostToolUse` to see failed tool calls.
1005
1016
  - `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing.
1006
1017
  - `onSessionStart` - Run logic when a session starts or resumes.
1007
1018
  - `onSessionEnd` - Cleanup or logging when session ends.
package/dist/canvas.d.ts CHANGED
@@ -1,15 +1,15 @@
1
+ import type { CanvasJsonSchema, CanvasProviderCloseRequest, CanvasProviderInvokeActionRequest, CanvasProviderOpenRequest, CanvasProviderOpenResult } from "./generated/rpc.js";
2
+ export type { CanvasJsonSchema, CanvasHostContext } from "./generated/rpc.js";
1
3
  /**
2
4
  * Extension-owned canvases declared via
3
5
  * `joinSession({ canvases: [createCanvas({...})] })`.
4
6
  *
5
- * The runtime sends provider callbacks directly as `canvas.open`,
6
- * `canvas.close`, and `canvas.action.invoke` JSON-RPC requests. The SDK
7
- * routes those requests by `canvasId` to the in-process handlers bound by
8
- * `createCanvas`. Re-opening with an existing `instanceId` is how the host
9
- * focuses an existing panel; reload is a renderer-only concern.
7
+ * The runtime sends provider callbacks as `canvas.open`, `canvas.close`, and
8
+ * `canvas.invokeAction` JSON-RPC requests via the codegen client session API
9
+ * pipeline. The SDK routes those requests by `canvasId` to the in-process
10
+ * handlers bound by `createCanvas`. Re-opening with an existing `instanceId`
11
+ * is how the host focuses an existing panel; reload is a renderer-only concern.
10
12
  */
11
- /** JSON Schema object used for canvas inputs. */
12
- export type CanvasJsonSchema = Record<string, unknown>;
13
13
  /**
14
14
  * A single agent-callable action contributed by a canvas. The metadata
15
15
  * (`name`, `description`, `inputSchema`) is serialized over the wire on
@@ -27,7 +27,7 @@ export interface CanvasAction {
27
27
  /** Optional JSON Schema for the action's `input` payload. */
28
28
  inputSchema?: CanvasJsonSchema;
29
29
  /** Required per-action dispatch handler. */
30
- handler: (ctx: CanvasActionContext) => Promise<unknown> | unknown;
30
+ handler: (ctx: CanvasProviderInvokeActionRequest) => Promise<unknown> | unknown;
31
31
  }
32
32
  /**
33
33
  * Declarative metadata for a single canvas, serialized over the wire on
@@ -45,66 +45,6 @@ export interface CanvasDeclaration {
45
45
  /** Agent-invocable actions exposed via `invoke_canvas_action`. */
46
46
  actions?: Omit<CanvasAction, "handler">[];
47
47
  }
48
- /** Response returned from `open`. */
49
- export interface CanvasOpenResponse {
50
- /** URL the host should render. Optional for native canvases. */
51
- url?: string;
52
- /** Provider-supplied title shown in host chrome. */
53
- title?: string;
54
- /** Provider-supplied status text shown in host chrome. */
55
- status?: string;
56
- }
57
- /** Host capabilities passed to canvas callbacks. */
58
- export interface CanvasHostContext {
59
- capabilities?: {
60
- canvases?: boolean;
61
- };
62
- }
63
- /** Context handed to a canvas's `open` handler. */
64
- export interface CanvasOpenContext {
65
- /** Session that requested the canvas. */
66
- sessionId: string;
67
- /** Extension id that owns the canvas. */
68
- extensionId: string;
69
- /** Canvas id (matches the declaring `CanvasDeclaration.id`). */
70
- canvasId: string;
71
- /** Stable instance id supplied by the runtime. */
72
- instanceId: string;
73
- /** Validated `input` payload, shaped by `CanvasDeclaration.inputSchema`. */
74
- input: unknown;
75
- /** Host capabilities supplied by the runtime. */
76
- host?: CanvasHostContext;
77
- }
78
- /** Context handed to a canvas action handler. */
79
- export interface CanvasActionContext {
80
- /** Session that invoked the action. */
81
- sessionId: string;
82
- /** Extension id that owns the canvas. */
83
- extensionId: string;
84
- /** Canvas id targeted by the action. */
85
- canvasId: string;
86
- /** Instance id targeted by the action. */
87
- instanceId: string;
88
- /** Action name from `CanvasAction.name`. */
89
- actionName: string;
90
- /** Validated `input` payload, shaped by the action's `inputSchema`. */
91
- input: unknown;
92
- /** Host capabilities supplied by the runtime. */
93
- host?: CanvasHostContext;
94
- }
95
- /** Context handed to a canvas's `onClose` handler. */
96
- export interface CanvasLifecycleContext {
97
- /** Session owning the canvas instance. */
98
- sessionId: string;
99
- /** Extension id that owns the canvas. */
100
- extensionId: string;
101
- /** Canvas id (matches the declaring `CanvasDeclaration.id`). */
102
- canvasId: string;
103
- /** Instance id this lifecycle event applies to. */
104
- instanceId: string;
105
- /** Host capabilities supplied by the runtime. */
106
- host?: CanvasHostContext;
107
- }
108
48
  /** Structured error returned from canvas handlers. */
109
49
  export declare class CanvasError extends Error {
110
50
  readonly code: string;
@@ -132,13 +72,13 @@ export interface CanvasOptions {
132
72
  */
133
73
  actions?: CanvasAction[];
134
74
  /** Required. Open a new canvas instance. */
135
- open: (ctx: CanvasOpenContext) => Promise<CanvasOpenResponse> | CanvasOpenResponse;
75
+ open: (ctx: CanvasProviderOpenRequest) => Promise<CanvasProviderOpenResult> | CanvasProviderOpenResult;
136
76
  /**
137
77
  * Optional. Notified when a canvas instance is closed by the user, the
138
78
  * agent, or the host. Fire-and-forget: the return value is ignored and
139
79
  * errors are logged but not surfaced to the runtime.
140
80
  */
141
- onClose?: (ctx: CanvasLifecycleContext) => Promise<void> | void;
81
+ onClose?: (ctx: CanvasProviderCloseRequest) => Promise<void> | void;
142
82
  }
143
83
  /** A registered canvas: declarative metadata + in-process handler closures.
144
84
  *
package/dist/canvas.js CHANGED
@@ -42,51 +42,8 @@ class Canvas {
42
42
  function createCanvas(options) {
43
43
  return new Canvas(options);
44
44
  }
45
- async function dispatchCanvasProviderRequest(canvas, actionName, params) {
46
- switch (actionName) {
47
- case "canvas.open": {
48
- const result = await canvas.open({
49
- sessionId: params.sessionId,
50
- extensionId: params.extensionId,
51
- canvasId: params.canvasId,
52
- instanceId: params.instanceId,
53
- input: params.input,
54
- host: params.host
55
- });
56
- return result ?? {};
57
- }
58
- case "canvas.close": {
59
- if (canvas.onClose) {
60
- await canvas.onClose({
61
- sessionId: params.sessionId,
62
- extensionId: params.extensionId,
63
- canvasId: params.canvasId,
64
- instanceId: params.instanceId,
65
- host: params.host
66
- });
67
- }
68
- return void 0;
69
- }
70
- default: {
71
- const perAction = canvas.actionHandlers.get(actionName);
72
- if (!perAction) {
73
- throw CanvasError.noHandler();
74
- }
75
- return perAction({
76
- sessionId: params.sessionId,
77
- extensionId: params.extensionId,
78
- canvasId: params.canvasId,
79
- instanceId: params.instanceId,
80
- actionName,
81
- input: params.input,
82
- host: params.host
83
- });
84
- }
85
- }
86
- }
87
45
  export {
88
46
  Canvas,
89
47
  CanvasError,
90
- createCanvas,
91
- dispatchCanvasProviderRequest
48
+ createCanvas
92
49
  };
@@ -20,8 +20,7 @@ var canvas_exports = {};
20
20
  __export(canvas_exports, {
21
21
  Canvas: () => Canvas,
22
22
  CanvasError: () => CanvasError,
23
- createCanvas: () => createCanvas,
24
- dispatchCanvasProviderRequest: () => dispatchCanvasProviderRequest
23
+ createCanvas: () => createCanvas
25
24
  });
26
25
  module.exports = __toCommonJS(canvas_exports);
27
26
  class CanvasError extends Error {
@@ -68,52 +67,9 @@ class Canvas {
68
67
  function createCanvas(options) {
69
68
  return new Canvas(options);
70
69
  }
71
- async function dispatchCanvasProviderRequest(canvas, actionName, params) {
72
- switch (actionName) {
73
- case "canvas.open": {
74
- const result = await canvas.open({
75
- sessionId: params.sessionId,
76
- extensionId: params.extensionId,
77
- canvasId: params.canvasId,
78
- instanceId: params.instanceId,
79
- input: params.input,
80
- host: params.host
81
- });
82
- return result ?? {};
83
- }
84
- case "canvas.close": {
85
- if (canvas.onClose) {
86
- await canvas.onClose({
87
- sessionId: params.sessionId,
88
- extensionId: params.extensionId,
89
- canvasId: params.canvasId,
90
- instanceId: params.instanceId,
91
- host: params.host
92
- });
93
- }
94
- return void 0;
95
- }
96
- default: {
97
- const perAction = canvas.actionHandlers.get(actionName);
98
- if (!perAction) {
99
- throw CanvasError.noHandler();
100
- }
101
- return perAction({
102
- sessionId: params.sessionId,
103
- extensionId: params.extensionId,
104
- canvasId: params.canvasId,
105
- instanceId: params.instanceId,
106
- actionName,
107
- input: params.input,
108
- host: params.host
109
- });
110
- }
111
- }
112
- }
113
70
  // Annotate the CommonJS export names for ESM import in node:
114
71
  0 && (module.exports = {
115
72
  Canvas,
116
73
  CanvasError,
117
- createCanvas,
118
- dispatchCanvasProviderRequest
74
+ createCanvas
119
75
  });
@@ -30,11 +30,11 @@ var import_node_path = require("node:path");
30
30
  var import_node_url = require("node:url");
31
31
  var import_node = require("vscode-jsonrpc/node.js");
32
32
  var import_rpc = require("./generated/rpc.js");
33
- var import_canvas = require("./canvas.js");
34
33
  var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
35
34
  var import_session = require("./session.js");
36
35
  var import_sessionFsProvider = require("./sessionFsProvider.js");
37
36
  var import_telemetry = require("./telemetry.js");
37
+ var import_toolSet = require("./toolSet.js");
38
38
  var import_types = require("./types.js");
39
39
  const import_meta = {};
40
40
  const MIN_PROTOCOL_VERSION = 3;
@@ -68,15 +68,23 @@ function toWireCustomAgents(agents) {
68
68
  return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
69
69
  });
70
70
  }
71
- function isCanvasProviderRequestParams(params) {
72
- if (!params || typeof params !== "object") {
73
- return false;
71
+ function toolFilterListToArray(value) {
72
+ if (value === void 0) {
73
+ return void 0;
74
74
  }
75
- const request = params;
76
- return typeof request.sessionId === "string" && typeof request.extensionId === "string" && typeof request.canvasId === "string" && typeof request.instanceId === "string";
75
+ return value instanceof import_toolSet.ToolSet ? value.toArray() : value;
77
76
  }
78
- function isCanvasActionInvokeParams(params) {
79
- return isCanvasProviderRequestParams(params) && typeof params.actionName === "string";
77
+ function validateToolFilterList(field, list) {
78
+ if (!list) {
79
+ return;
80
+ }
81
+ for (const entry of list) {
82
+ if (entry === "*") {
83
+ throw new Error(
84
+ `Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
85
+ );
86
+ }
87
+ }
80
88
  }
81
89
  function extractTransformCallbacks(systemMessage) {
82
90
  if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
@@ -260,8 +268,19 @@ class CopilotClient {
260
268
  telemetry: options.telemetry,
261
269
  baseDirectory: options.baseDirectory,
262
270
  sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
263
- enableRemoteSessions: options.enableRemoteSessions ?? false
271
+ enableRemoteSessions: options.enableRemoteSessions ?? false,
272
+ mode: options.mode ?? "copilot-cli"
264
273
  };
274
+ if (this.options.mode === "empty") {
275
+ const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
276
+ // can't enforce it from here.
277
+ conn.kind === "uri" || conn.kind === "parent-process";
278
+ if (!hasPersistence) {
279
+ throw new Error(
280
+ "CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
281
+ );
282
+ }
283
+ }
265
284
  }
266
285
  connectionExtraArgs = [];
267
286
  /**
@@ -571,10 +590,122 @@ class CopilotClient {
571
590
  * });
572
591
  * ```
573
592
  */
593
+ /**
594
+ * Normalizes session-level tool filter options. Converts {@link ToolSet}
595
+ * instances to plain string arrays, rejects misuse (bare `"*"`) and the
596
+ * missing-availableTools case in `mode = "empty"`.
597
+ *
598
+ * The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
599
+ * compose include + exclude lists naturally (e.g. "everything matching X
600
+ * except Y") regardless of mode. Allowlist-precedence is intentionally not
601
+ * exposed — it's available on the runtime side as a CLI-only concession to
602
+ * legacy behavior, but SDK consumers always get the composable semantics.
603
+ *
604
+ * @internal
605
+ */
606
+ resolveToolFilterOptions(config) {
607
+ const availableTools = toolFilterListToArray(config.availableTools);
608
+ const excludedTools = toolFilterListToArray(config.excludedTools);
609
+ validateToolFilterList("availableTools", availableTools);
610
+ validateToolFilterList("excludedTools", excludedTools);
611
+ if (this.options.mode === "empty") {
612
+ if (availableTools === void 0) {
613
+ throw new Error(
614
+ "CopilotClient is in mode: 'empty' but the session config did not specify 'availableTools'. Empty mode requires every session to explicitly opt into the tools it wants \u2014 e.g. `new ToolSet().addBuiltIn(BuiltInTools.Isolated)`."
615
+ );
616
+ }
617
+ }
618
+ return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
619
+ }
620
+ /** Mode-specific defaults spread under the caller's config (app values win). */
621
+ configDefaultsForMode() {
622
+ if (this.options.mode === "empty") {
623
+ return { enableSessionTelemetry: false };
624
+ }
625
+ return {};
626
+ }
627
+ /**
628
+ * Returns the systemMessage config to use, adjusted for the current mode.
629
+ * In empty mode we ensure the environment_context section is removed
630
+ * unless the app has already taken control of it. `append` (and
631
+ * unspecified) mode is promoted to `customize` so we can also strip
632
+ * environment_context; the caller's `content` is preserved verbatim
633
+ * because the runtime appends it as additional instructions in both
634
+ * customize and append modes.
635
+ */
636
+ getSystemMessageConfigForMode(supplied) {
637
+ if (this.options.mode !== "empty") return supplied;
638
+ if (!supplied) {
639
+ return {
640
+ mode: "customize",
641
+ sections: { environment_context: { action: "remove" } }
642
+ };
643
+ }
644
+ switch (supplied.mode) {
645
+ case "replace":
646
+ return supplied;
647
+ case "customize":
648
+ if (supplied.sections?.environment_context) return supplied;
649
+ return {
650
+ ...supplied,
651
+ sections: {
652
+ ...supplied.sections,
653
+ environment_context: { action: "remove" }
654
+ }
655
+ };
656
+ case "append":
657
+ case void 0:
658
+ return {
659
+ mode: "customize",
660
+ content: supplied.content,
661
+ sections: { environment_context: { action: "remove" } }
662
+ };
663
+ }
664
+ }
665
+ /**
666
+ * Mode-specific options applied via session.options.update after create/resume.
667
+ *
668
+ * In empty mode, defaults the four overridable feature flags to safe values
669
+ * (caller values from `config` win). `installedPlugins=[]` is unconditional
670
+ * in empty mode — apps that need custom plugins should switch modes.
671
+ */
672
+ async updateSessionOptionsForMode(session, config) {
673
+ const patch = {};
674
+ if (this.options.mode === "empty") {
675
+ patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
676
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
677
+ patch.coauthorEnabled = config.coauthorEnabled ?? false;
678
+ patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
679
+ patch.installedPlugins = [];
680
+ } else {
681
+ if (config.skipCustomInstructions !== void 0)
682
+ patch.skipCustomInstructions = config.skipCustomInstructions;
683
+ if (config.customAgentsLocalOnly !== void 0)
684
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
685
+ if (config.coauthorEnabled !== void 0)
686
+ patch.coauthorEnabled = config.coauthorEnabled;
687
+ if (config.manageScheduleEnabled !== void 0)
688
+ patch.manageScheduleEnabled = config.manageScheduleEnabled;
689
+ }
690
+ if (Object.keys(patch).length === 0) {
691
+ return;
692
+ }
693
+ try {
694
+ await session.rpc.options.update(patch);
695
+ } catch (e) {
696
+ try {
697
+ await session.disconnect();
698
+ } catch {
699
+ }
700
+ throw e;
701
+ }
702
+ }
574
703
  async createSession(config) {
575
704
  if (!this.connection) {
576
705
  await this.start();
577
706
  }
707
+ config = { ...this.configDefaultsForMode(), ...config };
708
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
578
709
  const sessionId = config.sessionId ?? (0, import_node_crypto.randomUUID)();
579
710
  const session = new import_session.CopilotSession(
580
711
  sessionId,
@@ -612,6 +743,7 @@ class CopilotClient {
612
743
  }
613
744
  this.sessions.set(sessionId, session);
614
745
  this.setupSessionFs(session, config);
746
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
615
747
  try {
616
748
  const response = await this.connection.sendRequest("session.create", {
617
749
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
@@ -635,8 +767,9 @@ class CopilotClient {
635
767
  description: cmd.description
636
768
  })),
637
769
  systemMessage: wireSystemMessage,
638
- availableTools: config.availableTools,
639
- excludedTools: config.excludedTools,
770
+ availableTools: toolFilterOptions.availableTools,
771
+ excludedTools: toolFilterOptions.excludedTools,
772
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
640
773
  provider: config.provider,
641
774
  enableSessionTelemetry: config.enableSessionTelemetry,
642
775
  modelCapabilities: config.modelCapabilities,
@@ -667,6 +800,7 @@ class CopilotClient {
667
800
  const { workspacePath, capabilities } = response;
668
801
  session["_workspacePath"] = workspacePath;
669
802
  session.setCapabilities(capabilities);
803
+ await this.updateSessionOptionsForMode(session, config);
670
804
  } catch (e) {
671
805
  this.sessions.delete(sessionId);
672
806
  throw e;
@@ -726,6 +860,8 @@ class CopilotClient {
726
860
  if (config.hooks) {
727
861
  session.registerHooks(config.hooks);
728
862
  }
863
+ config = { ...this.configDefaultsForMode(), ...config };
864
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
729
865
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
730
866
  config.systemMessage
731
867
  );
@@ -737,6 +873,7 @@ class CopilotClient {
737
873
  }
738
874
  this.sessions.set(sessionId, session);
739
875
  this.setupSessionFs(session, config);
876
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
740
877
  try {
741
878
  const response = await this.connection.sendRequest("session.resume", {
742
879
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
@@ -745,8 +882,9 @@ class CopilotClient {
745
882
  model: config.model,
746
883
  reasoningEffort: config.reasoningEffort,
747
884
  systemMessage: wireSystemMessage,
748
- availableTools: config.availableTools,
749
- excludedTools: config.excludedTools,
885
+ availableTools: toolFilterOptions.availableTools,
886
+ excludedTools: toolFilterOptions.excludedTools,
887
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
750
888
  enableSessionTelemetry: config.enableSessionTelemetry,
751
889
  tools: config.tools?.map((tool) => ({
752
890
  name: tool.name,
@@ -795,6 +933,7 @@ class CopilotClient {
795
933
  session["_workspacePath"] = workspacePath;
796
934
  session.setCapabilities(capabilities);
797
935
  session.setOpenCanvases(openCanvases ?? []);
936
+ await this.updateSessionOptionsForMode(session, config);
798
937
  } catch (e) {
799
938
  this.sessions.delete(sessionId);
800
939
  throw e;
@@ -1173,6 +1312,9 @@ class CopilotClient {
1173
1312
  if (this.options.baseDirectory) {
1174
1313
  envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
1175
1314
  }
1315
+ if (this.options.mode === "empty") {
1316
+ envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
1317
+ }
1176
1318
  if (!this.resolvedCliPath) {
1177
1319
  throw new Error(
1178
1320
  "Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
@@ -1409,18 +1551,6 @@ stderr: ${stderrOutput}`
1409
1551
  "systemMessage.transform",
1410
1552
  async (params) => await this.handleSystemMessageTransform(params)
1411
1553
  );
1412
- this.connection.onRequest(
1413
- "canvas.open",
1414
- async (params) => this.handleCanvasProviderRequest("canvas.open", params)
1415
- );
1416
- this.connection.onRequest(
1417
- "canvas.close",
1418
- async (params) => this.handleCanvasProviderRequest("canvas.close", params)
1419
- );
1420
- this.connection.onRequest(
1421
- "canvas.action.invoke",
1422
- async (params) => this.handleCanvasActionInvokeRequest(params)
1423
- );
1424
1554
  const sessions = this.sessions;
1425
1555
  (0, import_rpc.registerClientSessionApiHandlers)(this.connection, (sessionId) => {
1426
1556
  const session = sessions.get(sessionId);
@@ -1542,26 +1672,6 @@ stderr: ${stderrOutput}`
1542
1672
  }
1543
1673
  return await session._handleSystemMessageTransform(params.sections);
1544
1674
  }
1545
- async handleCanvasProviderRequest(actionName, params) {
1546
- if (!isCanvasProviderRequestParams(params)) {
1547
- throw new Error("Invalid canvas provider request payload");
1548
- }
1549
- const session = this.sessions.get(params.sessionId);
1550
- if (!session) {
1551
- throw new Error(`Session not found: ${params.sessionId}`);
1552
- }
1553
- const canvas = session.getCanvas(params.canvasId);
1554
- if (!canvas) {
1555
- throw new Error(`No canvas registered with id "${params.canvasId}"`);
1556
- }
1557
- return (0, import_canvas.dispatchCanvasProviderRequest)(canvas, actionName, params);
1558
- }
1559
- async handleCanvasActionInvokeRequest(params) {
1560
- if (!isCanvasActionInvokeParams(params)) {
1561
- throw new Error("Invalid canvas provider request payload");
1562
- }
1563
- return this.handleCanvasProviderRequest(params.actionName, params);
1564
- }
1565
1675
  }
1566
1676
  // Annotate the CommonJS export names for ESM import in node:
1567
1677
  0 && (module.exports = {
@@ -171,7 +171,7 @@ function createServerRpc(connection) {
171
171
  /**
172
172
  * Lists persisted sessions, optionally filtered by working-directory context.
173
173
  *
174
- * @param params Optional metadata-load limit and context filter applied to the returned sessions.
174
+ * @param params Optional metadata-load limit and filters applied to the returned sessions.
175
175
  *
176
176
  * @returns Persisted sessions matching the filter, ordered most-recently-modified first.
177
177
  */
@@ -275,7 +275,7 @@ function createServerRpc(connection) {
275
275
  *
276
276
  * @param params Session metadata records to enrich with summary and context information.
277
277
  *
278
- * @returns The same metadata records, with summary and context fields backfilled where available.
278
+ * @returns The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted.
279
279
  */
280
280
  enrichMetadata: async (params) => connection.sendRequest("sessions.enrichMetadata", params),
281
281
  /**
@@ -302,6 +302,17 @@ function createServerRpc(connection) {
302
302
  * @returns Replace the manager-wide additional plugins. New session creations and subsequent hook reloads see the new set; already-running sessions keep their existing hook installation until the next reload.
303
303
  */
304
304
  setAdditionalPlugins: async (params) => connection.sendRequest("sessions.setAdditionalPlugins", params)
305
+ },
306
+ /** @experimental */
307
+ agentRegistry: {
308
+ /**
309
+ * Spawns a managed-server child with the supplied configuration and returns a discriminated-union result. The caller (typically the CLI controller) is responsible for attaching to the spawned child and sending any follow-up prompt. When the controller-local spawn gate is closed the server returns JSON-RPC MethodNotFound.
310
+ *
311
+ * @param params Inputs to spawn a managed-server child via the controller's spawn delegate.
312
+ *
313
+ * @returns Outcome of an agentRegistry.spawn call.
314
+ */
315
+ spawn: async (params) => connection.sendRequest("agentRegistry.spawn", params)
305
316
  }
306
317
  };
307
318
  }
@@ -538,7 +549,15 @@ function createSessionRpc(connection, sessionId) {
538
549
  *
539
550
  * @returns Descriptor for the saved paste file, or null when the workspace is unavailable.
540
551
  */
541
- saveLargePaste: async (params) => connection.sendRequest("session.workspaces.saveLargePaste", { sessionId, ...params })
552
+ saveLargePaste: async (params) => connection.sendRequest("session.workspaces.saveLargePaste", { sessionId, ...params }),
553
+ /**
554
+ * Computes a diff for the session workspace.
555
+ *
556
+ * @param params Parameters for computing a workspace diff.
557
+ *
558
+ * @returns Workspace diff result for the requested mode.
559
+ */
560
+ diff: async (params) => connection.sendRequest("session.workspaces.diff", { sessionId, ...params })
542
561
  },
543
562
  /** @experimental */
544
563
  instructions: {
@@ -1052,6 +1071,20 @@ function createSessionRpc(connection, sessionId) {
1052
1071
  * @returns Indicates whether the operation succeeded.
1053
1072
  */
1054
1073
  setApproveAll: async (params) => connection.sendRequest("session.permissions.setApproveAll", { sessionId, ...params }),
1074
+ /**
1075
+ * Enables or disables full allow-all permissions (tools, paths, and URLs) for the session. Used by attach-mode clients (e.g. LocalRpcSession's `/allow-all` forwarder) to flip the target session's permission state. Unlike `setApproveAll`, this swaps in the unrestricted path and URL managers and emits `session.permissions_changed` on transition. The result returns the authoritative post-mutation state so callers can update their local mirrors without racing the `session.permissions_changed` notification on the same wire.
1076
+ *
1077
+ * @param params Whether to enable full allow-all permissions for the session.
1078
+ *
1079
+ * @returns Indicates whether the operation succeeded and reports the post-mutation state.
1080
+ */
1081
+ setAllowAll: async (params) => connection.sendRequest("session.permissions.setAllowAll", { sessionId, ...params }),
1082
+ /**
1083
+ * Returns whether full allow-all permissions are currently active for the session.
1084
+ *
1085
+ * @returns Current full allow-all permission state.
1086
+ */
1087
+ getAllowAll: async () => connection.sendRequest("session.permissions.getAllowAll", { sessionId }),
1055
1088
  /**
1056
1089
  * Adds or removes session-scoped or location-scoped permission rules.
1057
1090
  *
@@ -1458,6 +1491,21 @@ function registerClientSessionApiHandlers(connection, getHandlers) {
1458
1491
  if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
1459
1492
  return handler.sqliteExists(params);
1460
1493
  });
1494
+ connection.onRequest("canvas.open", async (params) => {
1495
+ const handler = getHandlers(params.sessionId).canvas;
1496
+ if (!handler) throw new Error(`No canvas handler registered for session: ${params.sessionId}`);
1497
+ return handler.open(params);
1498
+ });
1499
+ connection.onRequest("canvas.close", async (params) => {
1500
+ const handler = getHandlers(params.sessionId).canvas;
1501
+ if (!handler) throw new Error(`No canvas handler registered for session: ${params.sessionId}`);
1502
+ return handler.close(params);
1503
+ });
1504
+ connection.onRequest("canvas.invokeAction", async (params) => {
1505
+ const handler = getHandlers(params.sessionId).canvas;
1506
+ if (!handler) throw new Error(`No canvas handler registered for session: ${params.sessionId}`);
1507
+ return handler.invokeAction(params);
1508
+ });
1461
1509
  }
1462
1510
  // Annotate the CommonJS export names for ESM import in node:
1463
1511
  0 && (module.exports = {