@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 +13 -2
- package/dist/canvas.d.ts +10 -70
- package/dist/canvas.js +1 -44
- package/dist/cjs/canvas.js +2 -46
- package/dist/cjs/client.js +155 -45
- package/dist/cjs/generated/rpc.js +51 -3
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/session.js +50 -11
- package/dist/cjs/toolSet.js +107 -0
- package/dist/client.d.ts +17 -27
- package/dist/client.js +155 -47
- package/dist/extension.d.ts +1 -1
- package/dist/generated/rpc.d.ts +766 -25
- package/dist/generated/rpc.js +51 -3
- package/dist/generated/session-events.d.ts +154 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -0
- package/dist/session.js +51 -12
- package/dist/toolSet.d.ts +75 -0
- package/dist/toolSet.js +82 -0
- package/dist/types.d.ts +134 -6
- package/docs/agent-author.md +31 -7
- package/docs/examples.md +20 -13
- package/package.json +2 -2
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
|
|
6
|
-
* `canvas.
|
|
7
|
-
* routes those requests by `canvasId` to the in-process
|
|
8
|
-
* `createCanvas`. Re-opening with an existing `instanceId`
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
};
|
package/dist/cjs/canvas.js
CHANGED
|
@@ -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
|
});
|
package/dist/cjs/client.js
CHANGED
|
@@ -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
|
|
72
|
-
if (
|
|
73
|
-
return
|
|
71
|
+
function toolFilterListToArray(value) {
|
|
72
|
+
if (value === void 0) {
|
|
73
|
+
return void 0;
|
|
74
74
|
}
|
|
75
|
-
|
|
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
|
|
79
|
-
|
|
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:
|
|
639
|
-
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:
|
|
749
|
-
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
|
|
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
|
|
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 = {
|