@github/copilot-language-server 1.551.1 → 1.551.3
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/main.js +748 -748
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +1 -1
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +499 -36
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/copilotRequestHandler.js +156 -20
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +11 -9
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/ffiRuntimeHost.js +14 -3
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/generated/rpc.js +1305 -304
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/host.js +67 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/index.js +12 -7
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/installationConfirmation.js +98 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/runtimeArtifacts.js +1 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/schema.js +40 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/session.js +474 -151
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/sessionFsProvider.js +57 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/types.js +5 -2
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/{factory.js → workflow.js} +33 -33
- package/dist/node_modules/@github/copilot-sdk/package.json +27 -16
- package/dist/node_modules/zod/locales/package.json +1 -2
- package/dist/node_modules/zod/mini/package.json +1 -2
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/node_modules/zod/v3/package.json +1 -2
- package/dist/node_modules/zod/v4/classic/errors.js +2 -2
- package/dist/node_modules/zod/v4/classic/from-json-schema.cjs +16 -31
- package/dist/node_modules/zod/v4/classic/from-json-schema.js +16 -31
- package/dist/node_modules/zod/v4/classic/package.json +1 -2
- package/dist/node_modules/zod/v4/classic/schemas.cjs +119 -358
- package/dist/node_modules/zod/v4/classic/schemas.js +118 -356
- package/dist/node_modules/zod/v4/core/api.cjs +2 -7
- package/dist/node_modules/zod/v4/core/api.js +2 -7
- package/dist/node_modules/zod/v4/core/core.cjs +1 -3
- package/dist/node_modules/zod/v4/core/core.js +2 -4
- package/dist/node_modules/zod/v4/core/errors.cjs +23 -26
- package/dist/node_modules/zod/v4/core/errors.js +23 -26
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +20 -16
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +20 -16
- package/dist/node_modules/zod/v4/core/package.json +1 -2
- package/dist/node_modules/zod/v4/core/parse.cjs +7 -7
- package/dist/node_modules/zod/v4/core/parse.js +7 -7
- package/dist/node_modules/zod/v4/core/regexes.cjs +3 -9
- package/dist/node_modules/zod/v4/core/regexes.js +1 -7
- package/dist/node_modules/zod/v4/core/schemas.cjs +65 -211
- package/dist/node_modules/zod/v4/core/schemas.js +64 -210
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +1 -12
- package/dist/node_modules/zod/v4/core/to-json-schema.js +1 -12
- package/dist/node_modules/zod/v4/core/util.cjs +30 -54
- package/dist/node_modules/zod/v4/core/util.js +32 -55
- package/dist/node_modules/zod/v4/core/versions.cjs +2 -2
- package/dist/node_modules/zod/v4/core/versions.js +2 -2
- package/dist/node_modules/zod/v4/locales/en.cjs +0 -4
- package/dist/node_modules/zod/v4/locales/en.js +0 -4
- package/dist/node_modules/zod/v4/locales/fr.cjs +7 -24
- package/dist/node_modules/zod/v4/locales/fr.js +7 -24
- package/dist/node_modules/zod/v4/locales/index.cjs +1 -8
- package/dist/node_modules/zod/v4/locales/index.js +0 -3
- package/dist/node_modules/zod/v4/locales/it.cjs +1 -1
- package/dist/node_modules/zod/v4/locales/it.js +1 -1
- package/dist/node_modules/zod/v4/locales/ka.cjs +8 -8
- package/dist/node_modules/zod/v4/locales/ka.js +8 -8
- package/dist/node_modules/zod/v4/locales/package.json +1 -2
- package/dist/node_modules/zod/v4/locales/uz.cjs +0 -1
- package/dist/node_modules/zod/v4/locales/uz.js +0 -1
- package/dist/node_modules/zod/v4/mini/package.json +1 -2
- package/dist/node_modules/zod/v4/mini/schemas.cjs +4 -41
- package/dist/node_modules/zod/v4/mini/schemas.js +4 -40
- package/dist/node_modules/zod/v4/package.json +1 -2
- package/dist/node_modules/zod/v4-mini/package.json +1 -2
- package/package.json +20 -20
- package/dist/node_modules/zod/v4/locales/el.cjs +0 -136
- package/dist/node_modules/zod/v4/locales/el.js +0 -109
- package/dist/node_modules/zod/v4/locales/hr.cjs +0 -149
- package/dist/node_modules/zod/v4/locales/hr.js +0 -122
- package/dist/node_modules/zod/v4/locales/ro.cjs +0 -146
- package/dist/node_modules/zod/v4/locales/ro.js +0 -119
|
@@ -26,28 +26,41 @@ var import_node = require("vscode-jsonrpc/node.js");
|
|
|
26
26
|
var import_rpc = require("./generated/rpc.js");
|
|
27
27
|
var import_canvas = require("./canvas.js");
|
|
28
28
|
var import_telemetry = require("./telemetry.js");
|
|
29
|
+
var import_schema = require("./schema.js");
|
|
29
30
|
var import_types = require("./types.js");
|
|
30
|
-
var
|
|
31
|
-
function
|
|
32
|
-
return value === "not_found" || value === "non_resumable" || value === "already_active" || value === "
|
|
31
|
+
var import_workflow = require("./workflow.js");
|
|
32
|
+
function isWorkflowResumeErrorCode(value) {
|
|
33
|
+
return value === "not_found" || value === "non_resumable" || value === "workflow_run_not_resumable" || value === "already_active" || value === "workflow_already_running" || value === "workflow_limits_invalid" || value === "workflow_session_disposed" || value === "workflow_storage_unavailable" || value === "workflow_storage_corrupt";
|
|
33
34
|
}
|
|
34
|
-
function
|
|
35
|
+
function copyDefinedWorkflowAgentOption(source, target, key) {
|
|
35
36
|
const value = source[key];
|
|
36
37
|
if (value !== void 0) {
|
|
37
38
|
target[key] = value;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
function toToolDefinition(tool) {
|
|
42
|
+
return {
|
|
43
|
+
name: tool.name,
|
|
44
|
+
description: tool.description ?? "",
|
|
45
|
+
parameters: (0, import_schema.toJsonSchema)(tool.parameters),
|
|
46
|
+
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
47
|
+
skipPermission: tool.skipPermission,
|
|
48
|
+
defer: tool.defer,
|
|
49
|
+
metadata: tool.metadata,
|
|
50
|
+
isTerminal: tool.isTerminal
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const workflowExecutionStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
54
|
+
function throwIfWorkflowExecutionIsActive() {
|
|
55
|
+
if (workflowExecutionStore.getStore()?.active) {
|
|
43
56
|
throw new Error(
|
|
44
|
-
"
|
|
57
|
+
"workflow.run, workflow.resume, and workflow.pause are not allowed while a workflow body is running on this call path."
|
|
45
58
|
);
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
|
-
function
|
|
49
|
-
const current =
|
|
50
|
-
return
|
|
61
|
+
function runInWorkflowHelperScope(helperScope, callback) {
|
|
62
|
+
const current = workflowExecutionStore.getStore();
|
|
63
|
+
return workflowExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
|
|
51
64
|
}
|
|
52
65
|
function deserializeHookInput(raw) {
|
|
53
66
|
if (!raw || typeof raw !== "object" || typeof raw.timestamp !== "number") {
|
|
@@ -69,22 +82,22 @@ function isOpenCanvasInstance(value) {
|
|
|
69
82
|
const instance = value;
|
|
70
83
|
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;
|
|
71
84
|
}
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
function
|
|
75
|
-
if (size >
|
|
85
|
+
const WORKFLOW_LOG_FLUSH_DELAY_MS = 10;
|
|
86
|
+
const MAX_WORKFLOW_FANOUT_ITEMS = 4096;
|
|
87
|
+
function assertWorkflowFanoutSize(kind, size) {
|
|
88
|
+
if (size > MAX_WORKFLOW_FANOUT_ITEMS) {
|
|
76
89
|
throw new Error(
|
|
77
|
-
`${kind}() accepts at most ${
|
|
90
|
+
`${kind}() accepts at most ${MAX_WORKFLOW_FANOUT_ITEMS} items; got ${size}.`
|
|
78
91
|
);
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
|
-
async function
|
|
94
|
+
async function runWorkflowParallel(thunks) {
|
|
82
95
|
if (!Array.isArray(thunks)) {
|
|
83
96
|
throw new Error(
|
|
84
97
|
"parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
|
|
85
98
|
);
|
|
86
99
|
}
|
|
87
|
-
|
|
100
|
+
assertWorkflowFanoutSize("parallel", thunks.length);
|
|
88
101
|
if (thunks.some((thunk) => typeof thunk !== "function")) {
|
|
89
102
|
throw new Error(
|
|
90
103
|
"parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
|
|
@@ -92,8 +105,8 @@ async function runFactoryParallel(thunks) {
|
|
|
92
105
|
}
|
|
93
106
|
return Promise.all(
|
|
94
107
|
thunks.map(
|
|
95
|
-
(thunk) => Promise.resolve().then(() =>
|
|
96
|
-
if (
|
|
108
|
+
(thunk) => Promise.resolve().then(() => runInWorkflowHelperScope("parallel", thunk)).catch((error) => {
|
|
109
|
+
if (isWorkflowFatalError(error)) {
|
|
97
110
|
throw error;
|
|
98
111
|
}
|
|
99
112
|
return null;
|
|
@@ -101,22 +114,22 @@ async function runFactoryParallel(thunks) {
|
|
|
101
114
|
)
|
|
102
115
|
);
|
|
103
116
|
}
|
|
104
|
-
async function
|
|
117
|
+
async function runWorkflowPipeline(items, ...stages) {
|
|
105
118
|
if (!Array.isArray(items)) {
|
|
106
119
|
throw new Error("pipeline(items, ...stages): items must be an array");
|
|
107
120
|
}
|
|
108
|
-
|
|
121
|
+
assertWorkflowFanoutSize("pipeline", items.length);
|
|
109
122
|
return Promise.all(
|
|
110
123
|
items.map(async (item, index) => {
|
|
111
124
|
let previous = item;
|
|
112
125
|
for (const stage of stages) {
|
|
113
126
|
try {
|
|
114
|
-
previous = await
|
|
127
|
+
previous = await runInWorkflowHelperScope(
|
|
115
128
|
"pipeline",
|
|
116
129
|
() => stage(previous, item, index)
|
|
117
130
|
);
|
|
118
131
|
} catch (error) {
|
|
119
|
-
if (
|
|
132
|
+
if (isWorkflowFatalError(error)) {
|
|
120
133
|
throw error;
|
|
121
134
|
}
|
|
122
135
|
return null;
|
|
@@ -126,7 +139,7 @@ async function runFactoryPipeline(items, ...stages) {
|
|
|
126
139
|
})
|
|
127
140
|
);
|
|
128
141
|
}
|
|
129
|
-
class
|
|
142
|
+
class WorkflowProgressBuffer {
|
|
130
143
|
constructor(send) {
|
|
131
144
|
this.send = send;
|
|
132
145
|
}
|
|
@@ -140,7 +153,7 @@ class FactoryProgressBuffer {
|
|
|
140
153
|
closed = false;
|
|
141
154
|
enqueue(kind, text) {
|
|
142
155
|
if (this.closed) {
|
|
143
|
-
throw new Error("Cannot log after the
|
|
156
|
+
throw new Error("Cannot log after the workflow run has settled");
|
|
144
157
|
}
|
|
145
158
|
this.pending.push({ seq: this.nextSeq++, kind, text });
|
|
146
159
|
this.scheduleFlush();
|
|
@@ -172,7 +185,7 @@ class FactoryProgressBuffer {
|
|
|
172
185
|
await this.flushTail;
|
|
173
186
|
if (this.flushFailed) {
|
|
174
187
|
console.warn(
|
|
175
|
-
"Ignoring a background
|
|
188
|
+
"Ignoring a background workflow progress flush failure after the workflow body settled",
|
|
176
189
|
this.flushError
|
|
177
190
|
);
|
|
178
191
|
}
|
|
@@ -181,7 +194,7 @@ class FactoryProgressBuffer {
|
|
|
181
194
|
await this.send(lines);
|
|
182
195
|
} catch (error) {
|
|
183
196
|
console.warn(
|
|
184
|
-
"Failed to flush final
|
|
197
|
+
"Failed to flush final workflow progress after the workflow body settled",
|
|
185
198
|
error
|
|
186
199
|
);
|
|
187
200
|
}
|
|
@@ -195,7 +208,7 @@ class FactoryProgressBuffer {
|
|
|
195
208
|
this.flushTimer = void 0;
|
|
196
209
|
void this.flush().catch(() => {
|
|
197
210
|
});
|
|
198
|
-
},
|
|
211
|
+
}, WORKFLOW_LOG_FLUSH_DELAY_MS);
|
|
199
212
|
this.flushTimer.unref?.();
|
|
200
213
|
}
|
|
201
214
|
clearFlushTimer() {
|
|
@@ -205,30 +218,30 @@ class FactoryProgressBuffer {
|
|
|
205
218
|
}
|
|
206
219
|
}
|
|
207
220
|
}
|
|
208
|
-
async function
|
|
221
|
+
async function awaitWorkflowOperation(operation, signal) {
|
|
209
222
|
let rejectAbort;
|
|
210
223
|
const abortPromise = new Promise((_resolve, reject) => {
|
|
211
224
|
rejectAbort = reject;
|
|
212
225
|
});
|
|
213
|
-
const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("
|
|
226
|
+
const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("Workflow run was aborted", "AbortError"));
|
|
214
227
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
215
228
|
try {
|
|
216
|
-
|
|
229
|
+
throwIfWorkflowAborted(signal);
|
|
217
230
|
return await Promise.race([operation(), abortPromise]);
|
|
218
231
|
} finally {
|
|
219
232
|
signal.removeEventListener("abort", onAbort);
|
|
220
233
|
}
|
|
221
234
|
}
|
|
222
|
-
function
|
|
235
|
+
function throwIfWorkflowAborted(signal) {
|
|
223
236
|
if (signal.aborted) {
|
|
224
|
-
throw signal.reason ?? new DOMException("
|
|
237
|
+
throw signal.reason ?? new DOMException("Workflow run was aborted", "AbortError");
|
|
225
238
|
}
|
|
226
239
|
}
|
|
227
|
-
function
|
|
240
|
+
function isWorkflowAbortError(error) {
|
|
228
241
|
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
229
242
|
}
|
|
230
|
-
function
|
|
231
|
-
return
|
|
243
|
+
function isWorkflowFatalError(error) {
|
|
244
|
+
return isWorkflowAbortError(error) || error instanceof import_node.ResponseError || error instanceof import_node.ConnectionError;
|
|
232
245
|
}
|
|
233
246
|
const TOOL_SEARCH_TOOL_NAME = "tool_search_tool";
|
|
234
247
|
class CopilotSession {
|
|
@@ -253,21 +266,25 @@ class CopilotSession {
|
|
|
253
266
|
sessionId;
|
|
254
267
|
connection;
|
|
255
268
|
_workspacePath;
|
|
269
|
+
_transcriptRecovery;
|
|
256
270
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
257
271
|
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
258
272
|
toolHandlers = /* @__PURE__ */ new Map();
|
|
273
|
+
/** Settles once every earlier `setTools` call has finished. */
|
|
274
|
+
setToolsQueue = Promise.resolve();
|
|
259
275
|
pendingExternalTools = /* @__PURE__ */ new Map();
|
|
260
276
|
canvases = /* @__PURE__ */ new Map();
|
|
261
277
|
bearerTokenProviders = /* @__PURE__ */ new Map();
|
|
262
278
|
commandHandlers = /* @__PURE__ */ new Map();
|
|
263
|
-
|
|
264
|
-
|
|
279
|
+
workflows = /* @__PURE__ */ new Map();
|
|
280
|
+
workflowAbortControllers = /* @__PURE__ */ new Map();
|
|
265
281
|
permissionHandler;
|
|
266
282
|
mcpAuthHandler;
|
|
267
283
|
userInputHandler;
|
|
268
284
|
elicitationHandler;
|
|
269
285
|
exitPlanModeHandler;
|
|
270
286
|
autoModeSwitchHandler;
|
|
287
|
+
skillProvider;
|
|
271
288
|
hooks;
|
|
272
289
|
transformCallbacks;
|
|
273
290
|
_rpc = null;
|
|
@@ -277,28 +294,23 @@ class CopilotSession {
|
|
|
277
294
|
_capabilities = {};
|
|
278
295
|
openCanvasInstances = [];
|
|
279
296
|
disconnected = false;
|
|
297
|
+
pendingResponseWaits = /* @__PURE__ */ new Set();
|
|
298
|
+
pendingStructuredWaits = /* @__PURE__ */ new Set();
|
|
280
299
|
disconnecting = false;
|
|
281
300
|
onDisconnected;
|
|
282
301
|
/** @internal Client session API handlers, populated by CopilotClient during create/resume. */
|
|
283
302
|
clientSessionApis = {};
|
|
284
303
|
/**
|
|
285
|
-
* Friendly
|
|
304
|
+
* Friendly workflow API for running registered workflows by name or handle.
|
|
286
305
|
*
|
|
287
|
-
* @experimental Part of the experimental
|
|
306
|
+
* @experimental Part of the experimental Dynamic Workflows surface and may
|
|
288
307
|
* change or be removed in future SDK or CLI releases.
|
|
289
308
|
*/
|
|
290
|
-
|
|
309
|
+
workflow = {
|
|
291
310
|
run: (async (nameOrHandle, options) => {
|
|
292
|
-
|
|
293
|
-
const name = typeof nameOrHandle === "string" ? nameOrHandle : (0,
|
|
294
|
-
|
|
295
|
-
return this.factory.resume(options.resumeFromRunId, {
|
|
296
|
-
limits: options.limits,
|
|
297
|
-
notifyOnComplete: options.notifyOnComplete,
|
|
298
|
-
logPhaseNames: options.logPhaseNames
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
const envelope = await this.rpc.factory.run({
|
|
311
|
+
throwIfWorkflowExecutionIsActive();
|
|
312
|
+
const name = typeof nameOrHandle === "string" ? nameOrHandle : (0, import_workflow.getWorkflowDefinition)(nameOrHandle).meta.name;
|
|
313
|
+
const envelope = await this.rpc.workflow.run({
|
|
302
314
|
name,
|
|
303
315
|
args: options?.args === void 0 ? {} : options.args,
|
|
304
316
|
options: {
|
|
@@ -307,13 +319,13 @@ class CopilotSession {
|
|
|
307
319
|
logPhaseNames: options?.logPhaseNames
|
|
308
320
|
}
|
|
309
321
|
});
|
|
310
|
-
return this.
|
|
322
|
+
return this.settleWorkflowRun(envelope);
|
|
311
323
|
}),
|
|
312
324
|
resume: (async (runId, options) => {
|
|
313
|
-
|
|
325
|
+
throwIfWorkflowExecutionIsActive();
|
|
314
326
|
let response;
|
|
315
327
|
try {
|
|
316
|
-
response = await this.rpc.
|
|
328
|
+
response = await this.rpc.workflow.resume({
|
|
317
329
|
runId,
|
|
318
330
|
limits: options?.limits,
|
|
319
331
|
notifyOnComplete: options?.notifyOnComplete,
|
|
@@ -322,43 +334,36 @@ class CopilotSession {
|
|
|
322
334
|
} catch (error) {
|
|
323
335
|
if (error instanceof import_node.ResponseError && typeof error.data === "object" && error.data !== null) {
|
|
324
336
|
const code = error.data.code;
|
|
325
|
-
if (
|
|
326
|
-
throw new
|
|
337
|
+
if (isWorkflowResumeErrorCode(code)) {
|
|
338
|
+
throw new import_workflow.WorkflowResumeError(code, error.message);
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
throw error;
|
|
330
342
|
}
|
|
331
|
-
return this.
|
|
343
|
+
return this.settleWorkflowRun(response.run);
|
|
332
344
|
}),
|
|
333
|
-
getRun: async (runId) => this.rpc.
|
|
334
|
-
waitForRun: (runId, options) => this.
|
|
345
|
+
getRun: async (runId) => this.rpc.workflow.getRun({ runId }),
|
|
346
|
+
waitForRun: (runId, options) => this.waitForWorkflowRun(runId, options?.signal),
|
|
335
347
|
listRuns: (async (options) => {
|
|
336
|
-
const page = await this.rpc.
|
|
348
|
+
const page = await this.rpc.workflow.listRuns(options ?? {});
|
|
337
349
|
return options === void 0 ? page.runs : page;
|
|
338
350
|
}),
|
|
339
|
-
getRunDetail: (runId) => this.rpc.
|
|
340
|
-
getRunProgress: (runId, options = {}) => this.rpc.
|
|
351
|
+
getRunDetail: (runId) => this.rpc.workflow.getRunDetail({ runId }),
|
|
352
|
+
getRunProgress: (runId, options = {}) => this.rpc.workflow.getRunProgress({ runId, ...options }),
|
|
341
353
|
pause: async (runId) => {
|
|
342
|
-
|
|
343
|
-
return this.rpc.
|
|
354
|
+
throwIfWorkflowExecutionIsActive();
|
|
355
|
+
return this.rpc.workflow.pause({ runId });
|
|
344
356
|
},
|
|
345
|
-
cancel: async (runId) => this.rpc.
|
|
357
|
+
cancel: async (runId) => this.rpc.workflow.cancel({ runId })
|
|
346
358
|
};
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
*
|
|
350
|
-
* The CLI may answer `session.factory.run` and `session.factory.resume`
|
|
351
|
-
* before the run settles, so a non-terminal envelope is followed by a wait
|
|
352
|
-
* on the run's terminal state.
|
|
353
|
-
*/
|
|
354
|
-
settleFactoryRun(envelope) {
|
|
355
|
-
if ((0, import_factory.isFactoryRunTerminal)(envelope.status)) {
|
|
359
|
+
settleWorkflowRun(envelope) {
|
|
360
|
+
if ((0, import_workflow.isWorkflowRunTerminal)(envelope.status)) {
|
|
356
361
|
return Promise.resolve(envelope);
|
|
357
362
|
}
|
|
358
|
-
return this.
|
|
363
|
+
return this.waitForWorkflowRun(envelope.runId);
|
|
359
364
|
}
|
|
360
365
|
/**
|
|
361
|
-
* Resolve when a
|
|
366
|
+
* Resolve when a workflow run reaches a terminal status.
|
|
362
367
|
*
|
|
363
368
|
* The subscription is installed *before* the first read so a transition
|
|
364
369
|
* landing between the two cannot be missed, and re-reads are serialized so
|
|
@@ -367,8 +372,8 @@ class CopilotSession {
|
|
|
367
372
|
* collapse into a single in-flight read. A bounded periodic re-read keeps a
|
|
368
373
|
* dropped invalidation from leaving the wait pending forever.
|
|
369
374
|
*/
|
|
370
|
-
|
|
371
|
-
const abortError = () => signal?.reason ?? new DOMException("
|
|
375
|
+
waitForWorkflowRun(runId, signal) {
|
|
376
|
+
const abortError = () => signal?.reason ?? new DOMException("Workflow run wait was aborted", "AbortError");
|
|
372
377
|
if (signal?.aborted === true) {
|
|
373
378
|
return Promise.reject(abortError());
|
|
374
379
|
}
|
|
@@ -405,8 +410,8 @@ class CopilotSession {
|
|
|
405
410
|
try {
|
|
406
411
|
do {
|
|
407
412
|
rereadRequested = false;
|
|
408
|
-
const envelope = await this.rpc.
|
|
409
|
-
if ((0,
|
|
413
|
+
const envelope = await this.rpc.workflow.getRun({ runId });
|
|
414
|
+
if ((0, import_workflow.isWorkflowRunTerminal)(envelope.status)) {
|
|
410
415
|
finish(() => resolve(envelope));
|
|
411
416
|
return;
|
|
412
417
|
}
|
|
@@ -421,7 +426,7 @@ class CopilotSession {
|
|
|
421
426
|
onAbort = () => finish(() => reject(abortError()));
|
|
422
427
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
423
428
|
}
|
|
424
|
-
unsubscribe = this.on("
|
|
429
|
+
unsubscribe = this.on("workflow.run_updated", (event) => {
|
|
425
430
|
if (event.data.runId === runId) {
|
|
426
431
|
void read();
|
|
427
432
|
}
|
|
@@ -455,6 +460,14 @@ class CopilotSession {
|
|
|
455
460
|
get workspacePath() {
|
|
456
461
|
return this._workspacePath;
|
|
457
462
|
}
|
|
463
|
+
/** Recovery observed while loading this session; undefined on a clean resume. */
|
|
464
|
+
get transcriptRecovery() {
|
|
465
|
+
return this._transcriptRecovery;
|
|
466
|
+
}
|
|
467
|
+
/** @internal */
|
|
468
|
+
setTranscriptRecovery(recovery) {
|
|
469
|
+
this._transcriptRecovery = recovery;
|
|
470
|
+
}
|
|
458
471
|
/**
|
|
459
472
|
* Host capabilities reported when the session was created or resumed.
|
|
460
473
|
* Use this to check feature support before calling capability-gated APIs.
|
|
@@ -493,19 +506,61 @@ class CopilotSession {
|
|
|
493
506
|
attachments: options.attachments,
|
|
494
507
|
mode: options.mode,
|
|
495
508
|
agentMode: options.agentMode,
|
|
496
|
-
requestHeaders: options.requestHeaders
|
|
509
|
+
requestHeaders: options.requestHeaders,
|
|
510
|
+
...options.responseSchema ? {
|
|
511
|
+
responseFormat: {
|
|
512
|
+
type: "json_schema",
|
|
513
|
+
jsonSchema: {
|
|
514
|
+
name: "response",
|
|
515
|
+
strict: true,
|
|
516
|
+
schema: (0, import_schema.toJsonSchema)(options.responseSchema)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} : {}
|
|
497
520
|
});
|
|
498
521
|
return response.messageId;
|
|
499
522
|
}
|
|
500
|
-
async sendAndWait(optionsOrPrompt, timeout) {
|
|
523
|
+
async sendAndWait(optionsOrPrompt, schemaOrTimeout, timeout) {
|
|
501
524
|
const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
|
|
502
|
-
const
|
|
525
|
+
const typedSchema = (0, import_schema.isResponseSchema)(schemaOrTimeout) ? schemaOrTimeout : void 0;
|
|
526
|
+
if (schemaOrTimeout !== void 0 && typeof schemaOrTimeout !== "number" && !typedSchema) {
|
|
527
|
+
throw new TypeError(
|
|
528
|
+
"The second argument must be a timeout or a schema with toJSONSchema() and parse(). Pass raw JSON Schema in options.responseSchema instead."
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
const effectiveTimeout = (typeof schemaOrTimeout === "number" ? schemaOrTimeout : timeout) ?? 6e4;
|
|
532
|
+
if (typedSchema && options.responseSchema) {
|
|
533
|
+
throw new Error(
|
|
534
|
+
"Do not specify responseSchema in options when requesting a typed response."
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
if (typedSchema && options.mode === "immediate") {
|
|
538
|
+
throw new Error(
|
|
539
|
+
"Structured output cannot be requested on an immediate steering message."
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
if (typedSchema || options.responseSchema) {
|
|
543
|
+
const message = await this.sendAndWaitForStructuredMessage(
|
|
544
|
+
typedSchema ? { ...options, responseSchema: typedSchema } : options,
|
|
545
|
+
effectiveTimeout
|
|
546
|
+
);
|
|
547
|
+
if (typedSchema) {
|
|
548
|
+
if (!message) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
"The requested run completed without a structured assistant response."
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
return typedSchema.parse(JSON.parse(message.data.content));
|
|
554
|
+
}
|
|
555
|
+
return message;
|
|
556
|
+
}
|
|
503
557
|
let resolveOutcome;
|
|
504
558
|
const outcomePromise = new Promise((resolve) => {
|
|
505
559
|
resolveOutcome = resolve;
|
|
506
560
|
});
|
|
507
561
|
let lastAssistantMessage;
|
|
508
562
|
const unsubscribe = this.on((event) => {
|
|
563
|
+
if (event.agentId) return;
|
|
509
564
|
if (event.type === "assistant.message") {
|
|
510
565
|
lastAssistantMessage = event;
|
|
511
566
|
} else if (event.type === "session.idle" && event.data.mode !== "autopilot") {
|
|
@@ -516,6 +571,8 @@ class CopilotSession {
|
|
|
516
571
|
resolveOutcome({ kind: "error", error });
|
|
517
572
|
}
|
|
518
573
|
});
|
|
574
|
+
const fail = (error) => resolveOutcome({ kind: "error", error });
|
|
575
|
+
this.pendingResponseWaits.add(fail);
|
|
519
576
|
let timeoutId;
|
|
520
577
|
try {
|
|
521
578
|
await this.send(options);
|
|
@@ -539,6 +596,91 @@ class CopilotSession {
|
|
|
539
596
|
clearTimeout(timeoutId);
|
|
540
597
|
}
|
|
541
598
|
unsubscribe();
|
|
599
|
+
this.pendingResponseWaits.delete(fail);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
async sendAndWaitForStructuredMessage(options, timeout) {
|
|
603
|
+
if (this.disconnected) {
|
|
604
|
+
throw new Error("Session is disconnected");
|
|
605
|
+
}
|
|
606
|
+
let resolveOutcome;
|
|
607
|
+
const outcomePromise = new Promise((resolve) => {
|
|
608
|
+
resolveOutcome = resolve;
|
|
609
|
+
});
|
|
610
|
+
const fail = (error) => resolveOutcome({ kind: "error", error });
|
|
611
|
+
let messageId;
|
|
612
|
+
let consumed = false;
|
|
613
|
+
let lastMessage;
|
|
614
|
+
const buffered = [];
|
|
615
|
+
const observe = (event) => {
|
|
616
|
+
if (event.agentId) return;
|
|
617
|
+
if (event.type === "user.message" && event.data.messageId === messageId) {
|
|
618
|
+
consumed = true;
|
|
619
|
+
} else if (event.type === "assistant.message" && event.data.originatingMessageId === messageId) {
|
|
620
|
+
consumed = true;
|
|
621
|
+
lastMessage = event.data.toolRequests?.length ? void 0 : event;
|
|
622
|
+
} else if (consumed && event.type === "session.idle" && event.data.mode !== "autopilot") {
|
|
623
|
+
if (event.data.aborted) {
|
|
624
|
+
fail(
|
|
625
|
+
new Error(
|
|
626
|
+
"The requested run was aborted before a structured result was completed."
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
} else {
|
|
630
|
+
resolveOutcome({ kind: "idle", message: lastMessage });
|
|
631
|
+
}
|
|
632
|
+
} else if (consumed && event.type === "session.error") {
|
|
633
|
+
const error = new Error(event.data.message);
|
|
634
|
+
error.stack = event.data.stack;
|
|
635
|
+
fail(error);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
const unsubscribe = this.on((event) => {
|
|
639
|
+
if (event.type !== "user.message" && event.type !== "assistant.message" && event.type !== "session.idle" && event.type !== "session.error") {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (messageId === void 0) {
|
|
643
|
+
buffered.push(event);
|
|
644
|
+
} else {
|
|
645
|
+
observe(event);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
this.pendingStructuredWaits.add(fail);
|
|
649
|
+
const timer = setTimeout(
|
|
650
|
+
() => fail(new Error(`Timeout after ${timeout}ms waiting for the structured response`)),
|
|
651
|
+
timeout
|
|
652
|
+
);
|
|
653
|
+
try {
|
|
654
|
+
const sendOutcome = this.send(options).then(
|
|
655
|
+
(id) => {
|
|
656
|
+
if (!id) {
|
|
657
|
+
throw new Error(
|
|
658
|
+
"The runtime did not return a message ID for the structured send."
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
messageId = id;
|
|
662
|
+
for (const event of buffered) observe(event);
|
|
663
|
+
buffered.length = 0;
|
|
664
|
+
return outcomePromise;
|
|
665
|
+
},
|
|
666
|
+
(error) => ({
|
|
667
|
+
kind: "error",
|
|
668
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
669
|
+
})
|
|
670
|
+
);
|
|
671
|
+
const outcome = await Promise.race([sendOutcome, outcomePromise]);
|
|
672
|
+
if (outcome.kind === "error") throw outcome.error;
|
|
673
|
+
return outcome.message;
|
|
674
|
+
} finally {
|
|
675
|
+
clearTimeout(timer);
|
|
676
|
+
buffered.length = 0;
|
|
677
|
+
unsubscribe();
|
|
678
|
+
this.pendingStructuredWaits.delete(fail);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
assertConnected() {
|
|
682
|
+
if (this.disconnected) {
|
|
683
|
+
throw new Error("Session is disconnected");
|
|
542
684
|
}
|
|
543
685
|
}
|
|
544
686
|
/** @internal */
|
|
@@ -547,40 +689,62 @@ class CopilotSession {
|
|
|
547
689
|
return;
|
|
548
690
|
}
|
|
549
691
|
this.disconnected = true;
|
|
692
|
+
for (const fail of this.pendingResponseWaits) {
|
|
693
|
+
fail(new Error("Session disconnected while waiting for a response"));
|
|
694
|
+
}
|
|
695
|
+
this.pendingResponseWaits.clear();
|
|
696
|
+
for (const fail of this.pendingStructuredWaits) {
|
|
697
|
+
fail(new Error("Session disconnected while waiting for a structured response"));
|
|
698
|
+
}
|
|
699
|
+
this.pendingStructuredWaits.clear();
|
|
550
700
|
for (const controller of this.pendingExternalTools.values()) {
|
|
551
701
|
controller.abort();
|
|
552
702
|
}
|
|
553
703
|
this.pendingExternalTools.clear();
|
|
554
|
-
this._runOnDisconnected();
|
|
555
704
|
this.eventHandlers.clear();
|
|
556
705
|
this.typedEventHandlers.clear();
|
|
557
706
|
this.toolHandlers.clear();
|
|
558
707
|
this.permissionHandler = void 0;
|
|
708
|
+
this.mcpAuthHandler = void 0;
|
|
559
709
|
this.userInputHandler = void 0;
|
|
560
710
|
this.elicitationHandler = void 0;
|
|
561
711
|
this.exitPlanModeHandler = void 0;
|
|
562
712
|
this.autoModeSwitchHandler = void 0;
|
|
713
|
+
this.skillProvider = void 0;
|
|
714
|
+
this.hooks = void 0;
|
|
715
|
+
this.traceContextProvider = void 0;
|
|
716
|
+
this.bearerTokenProviders.clear();
|
|
563
717
|
this.commandHandlers.clear();
|
|
564
718
|
this.canvases.clear();
|
|
565
|
-
this.
|
|
566
|
-
for (const controllersForRun of this.
|
|
719
|
+
this.workflows.clear();
|
|
720
|
+
for (const controllersForRun of this.workflowAbortControllers.values()) {
|
|
567
721
|
for (const controller of controllersForRun.values()) {
|
|
568
722
|
controller.abort();
|
|
569
723
|
}
|
|
570
724
|
}
|
|
571
|
-
this.
|
|
572
|
-
this.transformCallbacks
|
|
725
|
+
this.workflowAbortControllers.clear();
|
|
726
|
+
this.transformCallbacks = void 0;
|
|
727
|
+
delete this.clientSessionApis.providerToken;
|
|
728
|
+
delete this.clientSessionApis.canvas;
|
|
729
|
+
delete this.clientSessionApis.workflow;
|
|
730
|
+
delete this.clientSessionApis.sessionFs;
|
|
731
|
+
this._runOnDisconnected();
|
|
573
732
|
}
|
|
574
733
|
/** @internal */
|
|
575
734
|
_runOnDisconnected() {
|
|
576
|
-
this.onDisconnected
|
|
735
|
+
const callback = this.onDisconnected;
|
|
577
736
|
this.onDisconnected = void 0;
|
|
737
|
+
callback?.();
|
|
578
738
|
}
|
|
579
739
|
/** @internal */
|
|
580
740
|
_setOnDisconnected(callback) {
|
|
741
|
+
this.assertConnected();
|
|
581
742
|
this.onDisconnected = callback;
|
|
582
743
|
}
|
|
583
744
|
on(eventTypeOrHandler, handler) {
|
|
745
|
+
if (this.disconnected) {
|
|
746
|
+
throw new Error("Session is disconnected");
|
|
747
|
+
}
|
|
584
748
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
585
749
|
const eventType = eventTypeOrHandler;
|
|
586
750
|
if (!this.typedEventHandlers.has(eventType)) {
|
|
@@ -651,7 +815,8 @@ class CopilotSession {
|
|
|
651
815
|
args,
|
|
652
816
|
handler,
|
|
653
817
|
traceparent,
|
|
654
|
-
tracestate
|
|
818
|
+
tracestate,
|
|
819
|
+
event.agentId || void 0
|
|
655
820
|
);
|
|
656
821
|
}
|
|
657
822
|
} else if (event.type === "external_tool.completed") {
|
|
@@ -742,7 +907,7 @@ class CopilotSession {
|
|
|
742
907
|
* Executes a tool handler and sends the result back via RPC.
|
|
743
908
|
* @internal
|
|
744
909
|
*/
|
|
745
|
-
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
|
|
910
|
+
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate, agentId) {
|
|
746
911
|
const controller = new AbortController();
|
|
747
912
|
if (this.disconnected || this.pendingExternalTools.has(requestId)) {
|
|
748
913
|
return;
|
|
@@ -780,7 +945,9 @@ class CopilotSession {
|
|
|
780
945
|
availableTools,
|
|
781
946
|
traceparent,
|
|
782
947
|
tracestate,
|
|
783
|
-
signal: controller.signal
|
|
948
|
+
signal: controller.signal,
|
|
949
|
+
agentId,
|
|
950
|
+
requestId
|
|
784
951
|
});
|
|
785
952
|
let result;
|
|
786
953
|
if (rawResult == null) {
|
|
@@ -941,13 +1108,27 @@ class CopilotSession {
|
|
|
941
1108
|
* @internal This method is typically called internally when creating a session with tools.
|
|
942
1109
|
*/
|
|
943
1110
|
registerTools(tools) {
|
|
1111
|
+
this.assertConnected();
|
|
944
1112
|
this.toolHandlers.clear();
|
|
945
1113
|
if (!tools) {
|
|
946
1114
|
return;
|
|
947
1115
|
}
|
|
948
1116
|
for (const tool of tools) {
|
|
949
1117
|
if (tool.handler) {
|
|
950
|
-
|
|
1118
|
+
const handler = tool.handler;
|
|
1119
|
+
if (tool.name === "apply_patch" && tool.overridesBuiltInTool && (0, import_schema.toJsonSchema)(tool.parameters)?.type === "string") {
|
|
1120
|
+
this.toolHandlers.set(tool.name, (args, invocation) => {
|
|
1121
|
+
if (typeof args === "string") {
|
|
1122
|
+
return handler(args, invocation);
|
|
1123
|
+
}
|
|
1124
|
+
if (typeof args === "object" && args !== null && "input" in args && typeof args.input === "string") {
|
|
1125
|
+
return handler(args.input, invocation);
|
|
1126
|
+
}
|
|
1127
|
+
throw new TypeError("apply_patch string override requires a string input");
|
|
1128
|
+
});
|
|
1129
|
+
} else {
|
|
1130
|
+
this.toolHandlers.set(tool.name, handler);
|
|
1131
|
+
}
|
|
951
1132
|
}
|
|
952
1133
|
}
|
|
953
1134
|
}
|
|
@@ -968,6 +1149,7 @@ class CopilotSession {
|
|
|
968
1149
|
* @internal Called by the SDK when creating/resuming a session with `canvases`.
|
|
969
1150
|
*/
|
|
970
1151
|
registerCanvases(canvases) {
|
|
1152
|
+
this.assertConnected();
|
|
971
1153
|
this.canvases.clear();
|
|
972
1154
|
if (!canvases || canvases.length === 0) {
|
|
973
1155
|
delete this.clientSessionApis.canvas;
|
|
@@ -1017,46 +1199,47 @@ class CopilotSession {
|
|
|
1017
1199
|
};
|
|
1018
1200
|
}
|
|
1019
1201
|
/**
|
|
1020
|
-
* Registers
|
|
1202
|
+
* Registers workflow closures and reverse-RPC handlers for this session.
|
|
1021
1203
|
*
|
|
1022
|
-
* @param
|
|
1204
|
+
* @param workflows - Workflow handles declared by the joining extension.
|
|
1023
1205
|
* @internal Called by the SDK when an extension joins a session.
|
|
1024
1206
|
*/
|
|
1025
|
-
|
|
1026
|
-
this.
|
|
1027
|
-
|
|
1028
|
-
|
|
1207
|
+
registerWorkflows(workflows) {
|
|
1208
|
+
this.assertConnected();
|
|
1209
|
+
this.workflows.clear();
|
|
1210
|
+
if (!workflows || workflows.length === 0) {
|
|
1211
|
+
delete this.clientSessionApis.workflow;
|
|
1029
1212
|
return;
|
|
1030
1213
|
}
|
|
1031
|
-
for (const handle of
|
|
1032
|
-
const definition = (0,
|
|
1033
|
-
if (this.
|
|
1214
|
+
for (const handle of workflows) {
|
|
1215
|
+
const definition = (0, import_workflow.getWorkflowDefinition)(handle);
|
|
1216
|
+
if (this.workflows.has(definition.meta.name)) {
|
|
1034
1217
|
throw new Error(
|
|
1035
|
-
`Duplicate
|
|
1218
|
+
`Duplicate workflow name "${definition.meta.name}". Workflow names must be unique within a joinSession call.`
|
|
1036
1219
|
);
|
|
1037
1220
|
}
|
|
1038
|
-
this.
|
|
1221
|
+
this.workflows.set(definition.meta.name, definition);
|
|
1039
1222
|
}
|
|
1040
1223
|
const self = this;
|
|
1041
|
-
this.clientSessionApis.
|
|
1224
|
+
this.clientSessionApis.workflow = {
|
|
1042
1225
|
async execute(params) {
|
|
1043
|
-
const definition = self.
|
|
1226
|
+
const definition = self.workflows.get(params.name);
|
|
1044
1227
|
if (!definition) {
|
|
1045
|
-
const message = `No
|
|
1228
|
+
const message = `No workflow registered with name "${params.name}"`;
|
|
1046
1229
|
throw new import_node.ResponseError(import_node.ErrorCodes.InvalidParams, message, {
|
|
1047
|
-
code: "
|
|
1230
|
+
code: "workflow_not_found",
|
|
1048
1231
|
name: params.name
|
|
1049
1232
|
});
|
|
1050
1233
|
}
|
|
1051
1234
|
const controller = new AbortController();
|
|
1052
|
-
let controllersForRun = self.
|
|
1235
|
+
let controllersForRun = self.workflowAbortControllers.get(params.runId);
|
|
1053
1236
|
if (controllersForRun === void 0) {
|
|
1054
1237
|
controllersForRun = /* @__PURE__ */ new Map();
|
|
1055
|
-
self.
|
|
1238
|
+
self.workflowAbortControllers.set(params.runId, controllersForRun);
|
|
1056
1239
|
}
|
|
1057
1240
|
controllersForRun.set(params.executionToken, controller);
|
|
1058
|
-
const progress = new
|
|
1059
|
-
await self.rpc.
|
|
1241
|
+
const progress = new WorkflowProgressBuffer(async (lines) => {
|
|
1242
|
+
await self.rpc.workflow.log({
|
|
1060
1243
|
runId: params.runId,
|
|
1061
1244
|
executionToken: params.executionToken,
|
|
1062
1245
|
lines
|
|
@@ -1069,22 +1252,22 @@ class CopilotSession {
|
|
|
1069
1252
|
session: self,
|
|
1070
1253
|
signal: controller.signal,
|
|
1071
1254
|
phase: (title) => {
|
|
1072
|
-
|
|
1255
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1073
1256
|
progress.enqueue("phase", title);
|
|
1074
1257
|
},
|
|
1075
1258
|
log: (message) => {
|
|
1076
|
-
|
|
1259
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1077
1260
|
progress.enqueue("log", message);
|
|
1078
1261
|
},
|
|
1079
1262
|
agent: async (prompt, options = {}) => {
|
|
1080
1263
|
await progress.flush();
|
|
1081
1264
|
const opts = {};
|
|
1082
|
-
for (const key of
|
|
1083
|
-
|
|
1265
|
+
for (const key of import_workflow.WORKFLOW_AGENT_OPTION_KEYS) {
|
|
1266
|
+
copyDefinedWorkflowAgentOption(options, opts, key);
|
|
1084
1267
|
}
|
|
1085
|
-
const response = await
|
|
1086
|
-
() => self.rpc.
|
|
1087
|
-
|
|
1268
|
+
const response = await awaitWorkflowOperation(
|
|
1269
|
+
() => self.rpc.workflow.agent({
|
|
1270
|
+
workflowRunId: params.runId,
|
|
1088
1271
|
executionToken: params.executionToken,
|
|
1089
1272
|
prompt,
|
|
1090
1273
|
opts
|
|
@@ -1096,11 +1279,11 @@ class CopilotSession {
|
|
|
1096
1279
|
step: async (key, producer, options = {}) => {
|
|
1097
1280
|
await progress.flush();
|
|
1098
1281
|
if (options.volatile) {
|
|
1099
|
-
|
|
1282
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1100
1283
|
return producer();
|
|
1101
1284
|
}
|
|
1102
|
-
const cached = await
|
|
1103
|
-
() => self.rpc.
|
|
1285
|
+
const cached = await awaitWorkflowOperation(
|
|
1286
|
+
() => self.rpc.workflow.journal.get({
|
|
1104
1287
|
runId: params.runId,
|
|
1105
1288
|
executionToken: params.executionToken,
|
|
1106
1289
|
key
|
|
@@ -1113,13 +1296,13 @@ class CopilotSession {
|
|
|
1113
1296
|
`step("${key}") journal returned a hit without a result`
|
|
1114
1297
|
);
|
|
1115
1298
|
}
|
|
1116
|
-
|
|
1299
|
+
assertWorkflowStepResult(cached.resultJson, key);
|
|
1117
1300
|
return cached.resultJson;
|
|
1118
1301
|
}
|
|
1119
1302
|
const result2 = await producer();
|
|
1120
|
-
|
|
1121
|
-
await
|
|
1122
|
-
() => self.rpc.
|
|
1303
|
+
assertWorkflowStepResult(result2, key);
|
|
1304
|
+
await awaitWorkflowOperation(
|
|
1305
|
+
() => self.rpc.workflow.journal.put({
|
|
1123
1306
|
runId: params.runId,
|
|
1124
1307
|
executionToken: params.executionToken,
|
|
1125
1308
|
key,
|
|
@@ -1131,17 +1314,17 @@ class CopilotSession {
|
|
|
1131
1314
|
},
|
|
1132
1315
|
pause: async (key) => {
|
|
1133
1316
|
if (typeof key !== "string" || key.length === 0) {
|
|
1134
|
-
throw new Error("
|
|
1317
|
+
throw new Error("Workflow pause checkpoint key must not be empty");
|
|
1135
1318
|
}
|
|
1136
|
-
const helperScope =
|
|
1319
|
+
const helperScope = workflowExecutionStore.getStore()?.helperScope;
|
|
1137
1320
|
if (helperScope !== void 0) {
|
|
1138
1321
|
throw new Error(
|
|
1139
|
-
`
|
|
1322
|
+
`Workflow pause checkpoints are not allowed inside ${helperScope}() branches`
|
|
1140
1323
|
);
|
|
1141
1324
|
}
|
|
1142
1325
|
await progress.flush();
|
|
1143
|
-
const response = await
|
|
1144
|
-
() => self.internalRpc.
|
|
1326
|
+
const response = await awaitWorkflowOperation(
|
|
1327
|
+
() => self.internalRpc.workflow.pauseAtCheckpoint({
|
|
1145
1328
|
runId: params.runId,
|
|
1146
1329
|
executionToken: params.executionToken,
|
|
1147
1330
|
key
|
|
@@ -1152,21 +1335,21 @@ class CopilotSession {
|
|
|
1152
1335
|
case "continue":
|
|
1153
1336
|
return;
|
|
1154
1337
|
case "pause":
|
|
1155
|
-
await
|
|
1338
|
+
await awaitWorkflowOperation(
|
|
1156
1339
|
() => new Promise(() => {
|
|
1157
1340
|
}),
|
|
1158
1341
|
controller.signal
|
|
1159
1342
|
);
|
|
1160
1343
|
}
|
|
1161
1344
|
},
|
|
1162
|
-
parallel:
|
|
1163
|
-
pipeline:
|
|
1164
|
-
|
|
1165
|
-
throw new Error("nested
|
|
1345
|
+
parallel: runWorkflowParallel,
|
|
1346
|
+
pipeline: runWorkflowPipeline,
|
|
1347
|
+
workflow: async () => {
|
|
1348
|
+
throw new Error("nested workflows are not supported");
|
|
1166
1349
|
}
|
|
1167
1350
|
};
|
|
1168
1351
|
const execution = { active: true };
|
|
1169
|
-
const result = await
|
|
1352
|
+
const result = await workflowExecutionStore.run(execution, async () => {
|
|
1170
1353
|
try {
|
|
1171
1354
|
return await definition.run(context);
|
|
1172
1355
|
} finally {
|
|
@@ -1176,27 +1359,27 @@ class CopilotSession {
|
|
|
1176
1359
|
if (result === void 0) {
|
|
1177
1360
|
return {};
|
|
1178
1361
|
}
|
|
1179
|
-
|
|
1362
|
+
assertWorkflowResult(result);
|
|
1180
1363
|
return { result };
|
|
1181
1364
|
} finally {
|
|
1182
1365
|
try {
|
|
1183
1366
|
await progress.close();
|
|
1184
1367
|
} finally {
|
|
1185
|
-
const controllersForRun2 = self.
|
|
1368
|
+
const controllersForRun2 = self.workflowAbortControllers.get(params.runId);
|
|
1186
1369
|
if (controllersForRun2?.get(params.executionToken) === controller) {
|
|
1187
1370
|
controllersForRun2.delete(params.executionToken);
|
|
1188
1371
|
if (controllersForRun2.size === 0) {
|
|
1189
|
-
self.
|
|
1372
|
+
self.workflowAbortControllers.delete(params.runId);
|
|
1190
1373
|
}
|
|
1191
1374
|
}
|
|
1192
1375
|
}
|
|
1193
1376
|
}
|
|
1194
1377
|
},
|
|
1195
1378
|
async abort(params) {
|
|
1196
|
-
const controllersForRun = self.
|
|
1379
|
+
const controllersForRun = self.workflowAbortControllers.get(params.runId);
|
|
1197
1380
|
const controller = controllersForRun?.get(params.executionToken);
|
|
1198
1381
|
if (controller !== void 0) {
|
|
1199
|
-
controller.abort(new DOMException("
|
|
1382
|
+
controller.abort(new DOMException("Workflow run was aborted", "AbortError"));
|
|
1200
1383
|
}
|
|
1201
1384
|
return {};
|
|
1202
1385
|
}
|
|
@@ -1215,6 +1398,7 @@ class CopilotSession {
|
|
|
1215
1398
|
* @internal This method is called internally when creating/resuming a session.
|
|
1216
1399
|
*/
|
|
1217
1400
|
registerBearerTokenProviders(providers) {
|
|
1401
|
+
this.assertConnected();
|
|
1218
1402
|
this.bearerTokenProviders.clear();
|
|
1219
1403
|
if (!providers || providers.size === 0) {
|
|
1220
1404
|
delete this.clientSessionApis.providerToken;
|
|
@@ -1226,6 +1410,7 @@ class CopilotSession {
|
|
|
1226
1410
|
const self = this;
|
|
1227
1411
|
this.clientSessionApis.providerToken = {
|
|
1228
1412
|
async getToken(params) {
|
|
1413
|
+
self.assertConnected();
|
|
1229
1414
|
const callback = self.bearerTokenProviders.get(params.providerName);
|
|
1230
1415
|
if (!callback) {
|
|
1231
1416
|
throw new Error(
|
|
@@ -1247,6 +1432,7 @@ class CopilotSession {
|
|
|
1247
1432
|
* @internal This method is typically called internally when creating/resuming a session.
|
|
1248
1433
|
*/
|
|
1249
1434
|
registerCommands(commands) {
|
|
1435
|
+
this.assertConnected();
|
|
1250
1436
|
this.commandHandlers.clear();
|
|
1251
1437
|
if (!commands) {
|
|
1252
1438
|
return;
|
|
@@ -1262,6 +1448,7 @@ class CopilotSession {
|
|
|
1262
1448
|
* @internal This method is typically called internally when creating/resuming a session.
|
|
1263
1449
|
*/
|
|
1264
1450
|
registerElicitationHandler(handler) {
|
|
1451
|
+
this.assertConnected();
|
|
1265
1452
|
this.elicitationHandler = handler;
|
|
1266
1453
|
}
|
|
1267
1454
|
/**
|
|
@@ -1271,6 +1458,7 @@ class CopilotSession {
|
|
|
1271
1458
|
* @internal This method is typically called internally when creating/resuming a session.
|
|
1272
1459
|
*/
|
|
1273
1460
|
registerExitPlanModeHandler(handler) {
|
|
1461
|
+
this.assertConnected();
|
|
1274
1462
|
this.exitPlanModeHandler = handler;
|
|
1275
1463
|
}
|
|
1276
1464
|
/**
|
|
@@ -1280,6 +1468,7 @@ class CopilotSession {
|
|
|
1280
1468
|
* @internal This method is typically called internally when creating/resuming a session.
|
|
1281
1469
|
*/
|
|
1282
1470
|
registerAutoModeSwitchHandler(handler) {
|
|
1471
|
+
this.assertConnected();
|
|
1283
1472
|
this.autoModeSwitchHandler = handler;
|
|
1284
1473
|
}
|
|
1285
1474
|
/**
|
|
@@ -1318,6 +1507,7 @@ class CopilotSession {
|
|
|
1318
1507
|
* @internal
|
|
1319
1508
|
*/
|
|
1320
1509
|
async _handleExitPlanModeRequest(request) {
|
|
1510
|
+
this.assertConnected();
|
|
1321
1511
|
if (!this.exitPlanModeHandler) {
|
|
1322
1512
|
return { approved: true };
|
|
1323
1513
|
}
|
|
@@ -1328,6 +1518,7 @@ class CopilotSession {
|
|
|
1328
1518
|
* @internal
|
|
1329
1519
|
*/
|
|
1330
1520
|
async _handleAutoModeSwitchRequest(request) {
|
|
1521
|
+
this.assertConnected();
|
|
1331
1522
|
if (!this.autoModeSwitchHandler) {
|
|
1332
1523
|
return "no";
|
|
1333
1524
|
}
|
|
@@ -1439,6 +1630,7 @@ class CopilotSession {
|
|
|
1439
1630
|
* @internal This method is typically called internally when creating a session.
|
|
1440
1631
|
*/
|
|
1441
1632
|
registerPermissionHandler(handler) {
|
|
1633
|
+
this.assertConnected();
|
|
1442
1634
|
this.permissionHandler = handler;
|
|
1443
1635
|
}
|
|
1444
1636
|
/**
|
|
@@ -1451,8 +1643,73 @@ class CopilotSession {
|
|
|
1451
1643
|
* @internal This method is typically called internally when creating a session.
|
|
1452
1644
|
*/
|
|
1453
1645
|
registerUserInputHandler(handler) {
|
|
1646
|
+
this.assertConnected();
|
|
1454
1647
|
this.userInputHandler = handler;
|
|
1455
1648
|
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Registers the session's skill provider.
|
|
1651
|
+
*
|
|
1652
|
+
* @param provider - The skill provider, or undefined to remove it
|
|
1653
|
+
* @internal This method is typically called internally when creating a session.
|
|
1654
|
+
*/
|
|
1655
|
+
registerSkillProvider(provider) {
|
|
1656
|
+
this.assertConnected();
|
|
1657
|
+
this.skillProvider = provider;
|
|
1658
|
+
}
|
|
1659
|
+
/**
|
|
1660
|
+
* Handles a `skillProvider.list` request from the runtime.
|
|
1661
|
+
*
|
|
1662
|
+
* @internal This method is for internal use by the SDK.
|
|
1663
|
+
*/
|
|
1664
|
+
async _handleSkillProviderList(token) {
|
|
1665
|
+
const provider = this.requireSkillProvider();
|
|
1666
|
+
return await this.callSkillProvider("listSkills", token, async (options) => ({
|
|
1667
|
+
skills: await provider.listSkills(options) ?? []
|
|
1668
|
+
}));
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Handles a `skillProvider.read` request from the runtime.
|
|
1672
|
+
*
|
|
1673
|
+
* @internal This method is for internal use by the SDK.
|
|
1674
|
+
*/
|
|
1675
|
+
async _handleSkillProviderRead(name, token) {
|
|
1676
|
+
const provider = this.requireSkillProvider();
|
|
1677
|
+
return await this.callSkillProvider("readSkill", token, async (options) => ({
|
|
1678
|
+
markdown: await provider.readSkill(name, options) ?? null
|
|
1679
|
+
}));
|
|
1680
|
+
}
|
|
1681
|
+
requireSkillProvider() {
|
|
1682
|
+
this.assertConnected();
|
|
1683
|
+
if (!this.skillProvider) {
|
|
1684
|
+
throw new Error(`No skill provider for session: ${this.sessionId}`);
|
|
1685
|
+
}
|
|
1686
|
+
return this.skillProvider;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Runs a provider call with an abort signal tied to the runtime's request
|
|
1690
|
+
* cancellation. Failures are logged locally and reported generically.
|
|
1691
|
+
*/
|
|
1692
|
+
async callSkillProvider(operation, token, call) {
|
|
1693
|
+
const controller = new AbortController();
|
|
1694
|
+
const subscription = token?.onCancellationRequested(() => controller.abort());
|
|
1695
|
+
if (token?.isCancellationRequested) {
|
|
1696
|
+
controller.abort();
|
|
1697
|
+
}
|
|
1698
|
+
try {
|
|
1699
|
+
return await call({ signal: controller.signal });
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
if (controller.signal.aborted) {
|
|
1702
|
+
throw new import_node.ResponseError(-32800, `Skill provider ${operation} cancelled`);
|
|
1703
|
+
}
|
|
1704
|
+
console.error(`Skill provider ${operation} failed`, {
|
|
1705
|
+
sessionId: this.sessionId,
|
|
1706
|
+
error
|
|
1707
|
+
});
|
|
1708
|
+
throw skillProviderFailure(operation);
|
|
1709
|
+
} finally {
|
|
1710
|
+
subscription?.dispose();
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1456
1713
|
/**
|
|
1457
1714
|
* Registers hook handlers for session lifecycle events.
|
|
1458
1715
|
*
|
|
@@ -1463,6 +1720,7 @@ class CopilotSession {
|
|
|
1463
1720
|
* @internal This method is typically called internally when creating a session.
|
|
1464
1721
|
*/
|
|
1465
1722
|
registerHooks(hooks) {
|
|
1723
|
+
this.assertConnected();
|
|
1466
1724
|
this.hooks = hooks;
|
|
1467
1725
|
}
|
|
1468
1726
|
/**
|
|
@@ -1472,6 +1730,7 @@ class CopilotSession {
|
|
|
1472
1730
|
* @internal This method is typically called internally when creating a session.
|
|
1473
1731
|
*/
|
|
1474
1732
|
registerTransformCallbacks(callbacks) {
|
|
1733
|
+
this.assertConnected();
|
|
1475
1734
|
this.transformCallbacks = callbacks;
|
|
1476
1735
|
}
|
|
1477
1736
|
/**
|
|
@@ -1483,6 +1742,7 @@ class CopilotSession {
|
|
|
1483
1742
|
* @internal This method is for internal use by the SDK.
|
|
1484
1743
|
*/
|
|
1485
1744
|
async _handleSystemMessageTransform(sections) {
|
|
1745
|
+
this.assertConnected();
|
|
1486
1746
|
const result = {};
|
|
1487
1747
|
for (const [sectionId, { content }] of Object.entries(sections)) {
|
|
1488
1748
|
const callback = this.transformCallbacks?.get(sectionId);
|
|
@@ -1507,6 +1767,7 @@ class CopilotSession {
|
|
|
1507
1767
|
* @internal This method is for internal use by the SDK.
|
|
1508
1768
|
*/
|
|
1509
1769
|
async _handleUserInputRequest(request) {
|
|
1770
|
+
this.assertConnected();
|
|
1510
1771
|
if (!this.userInputHandler) {
|
|
1511
1772
|
throw new Error("User input requested but no handler registered");
|
|
1512
1773
|
}
|
|
@@ -1528,6 +1789,7 @@ class CopilotSession {
|
|
|
1528
1789
|
* @internal This method is for internal use by the SDK.
|
|
1529
1790
|
*/
|
|
1530
1791
|
async _handleHooksInvoke(hookType, input) {
|
|
1792
|
+
this.assertConnected();
|
|
1531
1793
|
if (!this.hooks) {
|
|
1532
1794
|
return void 0;
|
|
1533
1795
|
}
|
|
@@ -1542,7 +1804,9 @@ class CopilotSession {
|
|
|
1542
1804
|
sessionStart: this.hooks.onSessionStart,
|
|
1543
1805
|
sessionEnd: this.hooks.onSessionEnd,
|
|
1544
1806
|
errorOccurred: this.hooks.onErrorOccurred,
|
|
1545
|
-
agentStop: this.hooks.onAgentStop
|
|
1807
|
+
agentStop: this.hooks.onAgentStop,
|
|
1808
|
+
subagentStart: this.hooks.onSubagentStart,
|
|
1809
|
+
subagentStop: this.hooks.onSubagentStop
|
|
1546
1810
|
};
|
|
1547
1811
|
const handler = handlerMap[hookType];
|
|
1548
1812
|
if (!handler) {
|
|
@@ -1706,6 +1970,62 @@ class CopilotSession {
|
|
|
1706
1970
|
async setAutoTier(autoTier) {
|
|
1707
1971
|
return await this.rpc.model.switchAutoTier({ autoTier });
|
|
1708
1972
|
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Replace the tools this client supplies to the session.
|
|
1975
|
+
*
|
|
1976
|
+
* `tools` becomes the complete set of tools this client implements,
|
|
1977
|
+
* replacing the ones it supplied when the session was created or resumed,
|
|
1978
|
+
* or in an earlier call. Built-in, MCP, and plugin tools, and tools other
|
|
1979
|
+
* connected clients supply, are unaffected. Pass an empty array to remove
|
|
1980
|
+
* all of this client's tools.
|
|
1981
|
+
*
|
|
1982
|
+
* Tools are defined the same way as for `createSession`: calls to tools
|
|
1983
|
+
* with a `handler` are dispatched to it, and tools without one are
|
|
1984
|
+
* declaration-only. Once the runtime accepts the replacement, every tool
|
|
1985
|
+
* call this session dispatches uses the new handlers; calls already
|
|
1986
|
+
* running finish on their original handlers. If the runtime rejects the
|
|
1987
|
+
* replacement, this rejects and the previous tools and handlers stay in
|
|
1988
|
+
* place. Concurrent calls on the same session are applied one at a time,
|
|
1989
|
+
* in the order they are made.
|
|
1990
|
+
*
|
|
1991
|
+
* The agent sees the new tools from its next model request, which can fall
|
|
1992
|
+
* within a turn in progress. A model request already in flight was made
|
|
1993
|
+
* with the previous tools, so the agent can still call a tool you removed.
|
|
1994
|
+
* This session doesn't answer that call, and it can stay pending until the
|
|
1995
|
+
* turn is aborted. If a running turn might still call a tool you remove,
|
|
1996
|
+
* replace tools while the session is idle.
|
|
1997
|
+
*
|
|
1998
|
+
* @param tools - The complete set of tools this client supplies
|
|
1999
|
+
* @throws Error if the session disconnects before the replacement is applied
|
|
2000
|
+
*
|
|
2001
|
+
* @experimental Wraps the experimental `session.tools.set` RPC and may change
|
|
2002
|
+
* or be removed in a future release.
|
|
2003
|
+
*
|
|
2004
|
+
* @example
|
|
2005
|
+
* ```typescript
|
|
2006
|
+
* await session.setTools([
|
|
2007
|
+
* defineTool("search_issues", {
|
|
2008
|
+
* description: "Search the issues shown on the current page",
|
|
2009
|
+
* parameters: z.object({ query: z.string() }),
|
|
2010
|
+
* handler: async ({ query }) => searchIssues(query),
|
|
2011
|
+
* }),
|
|
2012
|
+
* ]);
|
|
2013
|
+
* ```
|
|
2014
|
+
*/
|
|
2015
|
+
async setTools(tools) {
|
|
2016
|
+
this.assertConnected();
|
|
2017
|
+
const definitions = tools.map(toToolDefinition);
|
|
2018
|
+
const replacement = this.setToolsQueue.then(async () => {
|
|
2019
|
+
this.assertConnected();
|
|
2020
|
+
await this.rpc.tools.set({ tools: definitions });
|
|
2021
|
+
this.registerTools(tools);
|
|
2022
|
+
});
|
|
2023
|
+
this.setToolsQueue = replacement.then(
|
|
2024
|
+
() => void 0,
|
|
2025
|
+
() => void 0
|
|
2026
|
+
);
|
|
2027
|
+
await replacement;
|
|
2028
|
+
}
|
|
1709
2029
|
/**
|
|
1710
2030
|
* Log a message to the session timeline.
|
|
1711
2031
|
* The message appears in the session event stream and is visible to SDK consumers
|
|
@@ -1751,6 +2071,9 @@ function toCanvasRpcError(error) {
|
|
|
1751
2071
|
const message = error instanceof Error ? error.message : String(error);
|
|
1752
2072
|
return new import_node.ResponseError(import_node.ErrorCodes.InternalError, message, { code, message });
|
|
1753
2073
|
}
|
|
2074
|
+
function skillProviderFailure(operation) {
|
|
2075
|
+
return new import_node.ResponseError(import_node.ErrorCodes.InternalError, `Skill provider ${operation} failed`);
|
|
2076
|
+
}
|
|
1754
2077
|
function strictJsonValidationError(context, category, message, path) {
|
|
1755
2078
|
return new import_node.ResponseError(import_node.ErrorCodes.InternalError, message, {
|
|
1756
2079
|
code: context.code,
|
|
@@ -1882,17 +2205,17 @@ function assertStrictJson(value, context) {
|
|
|
1882
2205
|
};
|
|
1883
2206
|
visit(value, "$", context.allowTopLevelUndefined);
|
|
1884
2207
|
}
|
|
1885
|
-
function
|
|
2208
|
+
function assertWorkflowResult(value) {
|
|
1886
2209
|
assertStrictJson(value, {
|
|
1887
|
-
code: "
|
|
1888
|
-
label: "
|
|
2210
|
+
code: "workflow_result_not_json",
|
|
2211
|
+
label: "Workflow result",
|
|
1889
2212
|
allowTopLevelUndefined: true
|
|
1890
2213
|
});
|
|
1891
2214
|
}
|
|
1892
|
-
function
|
|
2215
|
+
function assertWorkflowStepResult(value, key) {
|
|
1893
2216
|
assertStrictJson(value, {
|
|
1894
|
-
code: "
|
|
1895
|
-
label: `
|
|
2217
|
+
code: "workflow_step_not_json",
|
|
2218
|
+
label: `Workflow step "${key}" result`,
|
|
1896
2219
|
allowTopLevelUndefined: false
|
|
1897
2220
|
});
|
|
1898
2221
|
}
|