dsh-github-copilot 0.4.0-alpha.21 → 0.4.0-alpha.23
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/CHANGELOG.md +14 -0
- package/README.md +22 -16
- package/README.zh.md +22 -16
- package/deployment-baseline.json +22 -5
- package/docs/agent-readiness.md +1 -0
- package/docs/dual-model.md +9 -0
- package/docs/session-search-routing.md +14 -6
- package/lib/client.js +127 -61
- package/lib/client.js.map +1 -1
- package/lib/index.js +860 -498
- package/lib/remote.js +21 -1
- package/lib/routed-web.js +44 -5
- package/lib/types/dual-model-host.d.ts +3 -2
- package/lib/types/routed-web.d.ts +13 -1
- package/lib/types/search-routing-host.d.ts +19 -0
- package/lib/types/search-routing-policy.d.ts +18 -0
- package/lib/types/search-routing-remote.d.ts +20 -0
- package/lib/types/search-tool-routing.d.ts +22 -0
- package/lib/types/web-search-routing-card.d.ts +2 -1
- package/lib/types/web-search-routing-config.d.ts +3 -7
- package/package.json +5 -3
package/lib/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { C as currentSearchInitiator, D as GITHUB_COPILOT_CREDENTIAL_KEY, E as readCopilotCatalog, O as GITHUB_COPILOT_PREVIEW_PROVIDER_ID, S as currentChatRoute, T as isPluginPreviewProvider, _ as RESPONSES_WEB_SEARCH_TOOL_TYPE, a as COPILOT_HOSTED_SEARCH_PROVIDER_ID, b as candidatesForRoute, c as abortable$1, d as readBounded, f as applyRequestAuth, h as ANTHROPIC_WEB_SEARCH_TOOL_TYPE, i as describeSearchBackend, k as GITHUB_COPILOT_PROVIDER_ID, l as isAbortError, m as providerRequestHeaders, n as routeSessionSearch, o as GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID, p as normalizeRequestAuth, r as DescribedSearchFallbackError, s as createTraditionalSearchProvider, t as isCopilotSearchSelection, u as providerErrorMessage, v as SearchPlan, w as currentSearchSelection, x as sameCandidates, y as WEB_SEARCH_TOOL_TYPE } from "./search-routing-pFLux0W7.js";
|
|
2
2
|
import AuthorizationService from "@deepseek-ai/dsh-authorization";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { Service } from "@deepseek-ai/cordis";
|
|
5
4
|
import * as dshScope from "@deepseek-ai/dsh-scope";
|
|
6
5
|
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
7
6
|
import z from "@deepseek-ai/schemastery";
|
|
@@ -18,6 +17,46 @@ import { Config as Config$1, PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
|
18
17
|
import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "@earendil-works/pi-ai/api/github-copilot-headers";
|
|
19
18
|
//#region lib/types/dual-model-host.js
|
|
20
19
|
/** Optional plugin-owned planner/executor sessions; no Core/default-model mutation. */
|
|
20
|
+
var __runInitializers$2 = function(thisArg, initializers, value) {
|
|
21
|
+
var useValue = arguments.length > 2;
|
|
22
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
23
|
+
return useValue ? value : void 0;
|
|
24
|
+
};
|
|
25
|
+
var __esDecorate$2 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
26
|
+
function accept(f) {
|
|
27
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
28
|
+
return f;
|
|
29
|
+
}
|
|
30
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
31
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
32
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
33
|
+
var _, done = false;
|
|
34
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
35
|
+
var context = {};
|
|
36
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
37
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
38
|
+
context.addInitializer = function(f) {
|
|
39
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
40
|
+
extraInitializers.push(accept(f || null));
|
|
41
|
+
};
|
|
42
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
43
|
+
get: descriptor.get,
|
|
44
|
+
set: descriptor.set
|
|
45
|
+
} : descriptor[key], context);
|
|
46
|
+
if (kind === "accessor") {
|
|
47
|
+
if (result === void 0) continue;
|
|
48
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
49
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
50
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
51
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
52
|
+
} else if (_ = accept(result)) {
|
|
53
|
+
if (kind === "field") initializers.unshift(_);
|
|
54
|
+
else descriptor[key] = _;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
58
|
+
done = true;
|
|
59
|
+
};
|
|
21
60
|
const DUAL_MODEL_NAMESPACE = "github-copilot-dual-model";
|
|
22
61
|
const DUAL_MODEL_POLICY_EVENT = "github-copilot/dual-model-policy";
|
|
23
62
|
const DUAL_MODEL_PROJECTION = "githubCopilotDualModelPolicy";
|
|
@@ -233,525 +272,676 @@ function dedicatedAddress(agent) {
|
|
|
233
272
|
const owned = /^copilot-dual-[0-9a-f]{64}$/;
|
|
234
273
|
return owned.test(agent.id) || agent.session.header.origin === "subagent" && owned.test(agent.session.header.parentSession ?? "");
|
|
235
274
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
let GitHubCopilotDualModel = (() => {
|
|
276
|
+
let _classSuper = TypertRemoteService;
|
|
277
|
+
let _instanceExtraInitializers = [];
|
|
278
|
+
let _view_decorators;
|
|
279
|
+
let _save_decorators;
|
|
280
|
+
let _create_decorators;
|
|
281
|
+
return class GitHubCopilotDualModel extends _classSuper {
|
|
282
|
+
static {
|
|
283
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
284
|
+
_view_decorators = [Remote];
|
|
285
|
+
_save_decorators = [Remote];
|
|
286
|
+
_create_decorators = [Remote];
|
|
287
|
+
__esDecorate$2(this, null, _view_decorators, {
|
|
288
|
+
kind: "method",
|
|
289
|
+
name: "view",
|
|
290
|
+
static: false,
|
|
291
|
+
private: false,
|
|
292
|
+
access: {
|
|
293
|
+
has: (obj) => "view" in obj,
|
|
294
|
+
get: (obj) => obj.view
|
|
295
|
+
},
|
|
296
|
+
metadata: _metadata
|
|
297
|
+
}, null, _instanceExtraInitializers);
|
|
298
|
+
__esDecorate$2(this, null, _save_decorators, {
|
|
299
|
+
kind: "method",
|
|
300
|
+
name: "save",
|
|
301
|
+
static: false,
|
|
302
|
+
private: false,
|
|
303
|
+
access: {
|
|
304
|
+
has: (obj) => "save" in obj,
|
|
305
|
+
get: (obj) => obj.save
|
|
306
|
+
},
|
|
307
|
+
metadata: _metadata
|
|
308
|
+
}, null, _instanceExtraInitializers);
|
|
309
|
+
__esDecorate$2(this, null, _create_decorators, {
|
|
310
|
+
kind: "method",
|
|
311
|
+
name: "create",
|
|
312
|
+
static: false,
|
|
313
|
+
private: false,
|
|
314
|
+
access: {
|
|
315
|
+
has: (obj) => "create" in obj,
|
|
316
|
+
get: (obj) => obj.create
|
|
317
|
+
},
|
|
318
|
+
metadata: _metadata
|
|
319
|
+
}, null, _instanceExtraInitializers);
|
|
320
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
321
|
+
enumerable: true,
|
|
322
|
+
configurable: true,
|
|
323
|
+
writable: true,
|
|
324
|
+
value: _metadata
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
owner = __runInitializers$2(this, _instanceExtraInitializers);
|
|
328
|
+
lifetime = new AbortController();
|
|
329
|
+
creates = /* @__PURE__ */ new Map();
|
|
330
|
+
overlays = /* @__PURE__ */ new Map();
|
|
331
|
+
handles = /* @__PURE__ */ new Map();
|
|
332
|
+
settingsReady = false;
|
|
333
|
+
projectionReady = false;
|
|
334
|
+
registrationFailed = false;
|
|
335
|
+
constructor(ctx) {
|
|
336
|
+
super(ctx, "githubCopilotDualModel");
|
|
337
|
+
this.owner = { ctx };
|
|
338
|
+
ctx.inject(["settings"], (scope) => {
|
|
339
|
+
const settings = api$1(scope, "settings", [
|
|
340
|
+
"register",
|
|
341
|
+
"describe",
|
|
342
|
+
"replace"
|
|
343
|
+
]);
|
|
344
|
+
if (!settings) return;
|
|
345
|
+
try {
|
|
346
|
+
settings.register(DUAL_MODEL_NAMESPACE, configSchema);
|
|
347
|
+
this.settingsReady = true;
|
|
348
|
+
} catch {
|
|
349
|
+
this.registrationFailed = true;
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
scope.effect(() => () => {
|
|
353
|
+
this.settingsReady = false;
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
ctx.inject(["sessionProjections"], (scope) => {
|
|
357
|
+
const projections = api$1(scope, "sessionProjections", ["register", "stateOf"]);
|
|
358
|
+
if (!projections) return;
|
|
359
|
+
try {
|
|
360
|
+
projections.register(dualModelProjection);
|
|
361
|
+
this.projectionReady = true;
|
|
362
|
+
} catch {
|
|
363
|
+
this.registrationFailed = true;
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
scope.effect(() => () => {
|
|
367
|
+
this.projectionReady = false;
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
ctx.inject(["tools"], (scope) => {
|
|
371
|
+
api$1(scope, "tools", ["guard"])?.guard((exec) => exec.agent && dedicatedAddress(exec.agent) && !this.overlays.has(exec.agent) ? "DUAL_MODEL_UNSUPPORTED" : void 0);
|
|
372
|
+
});
|
|
373
|
+
ctx.on("agent/request", async ({ agent }, next) => {
|
|
374
|
+
const subject = agent;
|
|
375
|
+
if (dedicatedAddress(subject) && !this.overlays.has(subject)) {
|
|
376
|
+
this.restore(subject);
|
|
377
|
+
if (!this.overlays.has(subject)) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
378
|
+
}
|
|
379
|
+
return next();
|
|
380
|
+
}, { prepend: true });
|
|
381
|
+
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
382
|
+
const candidate = context.scope;
|
|
383
|
+
if (!candidate || !object$3(candidate.session) || !object$3(candidate.session.header) || !dedicatedAddress(candidate)) return next();
|
|
384
|
+
const rehydrating = !this.overlays.has(candidate);
|
|
385
|
+
if (rehydrating) this.restore(candidate);
|
|
386
|
+
const installed = this.overlays.get(candidate);
|
|
387
|
+
if (!installed) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
388
|
+
const result = await next(), { policy, role } = installed;
|
|
389
|
+
const allowed = new Set(role === "planner" ? policy.plannerTools : policy.executorTools);
|
|
390
|
+
const sectionName = "github-copilot:dual-model-role";
|
|
391
|
+
const schemas = rehydrating ? this.requireCapabilities().tools.schemas(candidate) : result.tools;
|
|
392
|
+
return {
|
|
393
|
+
...result,
|
|
394
|
+
sections: result.sections.some((section) => section.name === sectionName) ? result.sections : [...result.sections, {
|
|
395
|
+
name: sectionName,
|
|
396
|
+
text: roleText(policy, role)
|
|
397
|
+
}],
|
|
398
|
+
tools: schemas.filter((tool) => allowed.has(tool.name)),
|
|
399
|
+
variables: {
|
|
400
|
+
...result.variables,
|
|
401
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
402
|
+
model: role === "planner" ? policy.plannerModel : policy.executorModel
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}, { prepend: true });
|
|
406
|
+
ctx.on("agent/created", ({ agent }) => this.restore(agent));
|
|
407
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
408
|
+
const subject = agent;
|
|
409
|
+
const overlay = this.overlays.get(subject);
|
|
410
|
+
this.overlays.delete(subject);
|
|
411
|
+
if (this.handles.get(agent.id)?.agent === subject) this.handles.delete(agent.id);
|
|
412
|
+
if (overlay) overlay.dispose().catch(() => {});
|
|
413
|
+
});
|
|
414
|
+
ctx.effect(() => async () => {
|
|
415
|
+
this.lifetime.abort();
|
|
416
|
+
await Promise.allSettled([...this.creates.values()].map((value) => value.promise));
|
|
417
|
+
const roots = [...this.overlays].filter(([, value]) => value.role === "planner").map(([agent]) => agent);
|
|
418
|
+
await api$1(ctx, "subagents", ["drainContinuableDescendants"])?.drainContinuableDescendants(roots).catch(() => {});
|
|
419
|
+
for (const [agent] of this.overlays) agent.cancel({ kind: "disposed" });
|
|
420
|
+
await Promise.allSettled([...this.overlays.keys()].map((agent) => agent.whenIdle()));
|
|
421
|
+
await Promise.allSettled([...this.handles.values()].map((handle) => handle.dispose()));
|
|
422
|
+
await Promise.allSettled([...this.overlays.values()].map((overlay) => overlay.dispose()));
|
|
423
|
+
this.handles.clear();
|
|
424
|
+
this.overlays.clear();
|
|
425
|
+
});
|
|
426
|
+
for (const agent of api$1(ctx, "agents", ["list"])?.list() ?? []) try {
|
|
427
|
+
this.restore(agent);
|
|
428
|
+
} catch {}
|
|
429
|
+
}
|
|
430
|
+
capabilities() {
|
|
431
|
+
if (typeof dshScope.createScope !== "function" || this.lifetime.signal.aborted || this.registrationFailed || !this.settingsReady || !this.projectionReady) return void 0;
|
|
432
|
+
const agents = api$1(this.owner.ctx, "agents", [
|
|
433
|
+
"list",
|
|
434
|
+
"get",
|
|
435
|
+
"create",
|
|
436
|
+
"resume"
|
|
437
|
+
]);
|
|
438
|
+
const workspaces = api$1(this.owner.ctx, "workspaceRegistry", ["get", "list"]);
|
|
439
|
+
const settings = api$1(this.owner.ctx, "settings", [
|
|
251
440
|
"register",
|
|
252
441
|
"describe",
|
|
253
442
|
"replace"
|
|
254
443
|
]);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
ctx.on("agent/request", async ({ agent }, next) => {
|
|
285
|
-
const subject = agent;
|
|
286
|
-
if (dedicatedAddress(subject) && !this.overlays.has(subject)) {
|
|
287
|
-
this.restore(subject);
|
|
288
|
-
if (!this.overlays.has(subject)) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
289
|
-
}
|
|
290
|
-
return next();
|
|
291
|
-
}, { prepend: true });
|
|
292
|
-
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
293
|
-
const candidate = context.scope;
|
|
294
|
-
if (!candidate || !object$3(candidate.session) || !object$3(candidate.session.header) || !dedicatedAddress(candidate)) return next();
|
|
295
|
-
const rehydrating = !this.overlays.has(candidate);
|
|
296
|
-
if (rehydrating) this.restore(candidate);
|
|
297
|
-
const installed = this.overlays.get(candidate);
|
|
298
|
-
if (!installed) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
299
|
-
const result = await next(), { policy, role } = installed;
|
|
300
|
-
const allowed = new Set(role === "planner" ? policy.plannerTools : policy.executorTools);
|
|
301
|
-
const sectionName = "github-copilot:dual-model-role";
|
|
302
|
-
const schemas = rehydrating ? this.requireCapabilities().tools.schemas(candidate) : result.tools;
|
|
444
|
+
const projections = api$1(this.owner.ctx, "sessionProjections", ["register", "stateOf"]);
|
|
445
|
+
const persistence = api$1(this.owner.ctx, "sessionPersistence", ["stat"]);
|
|
446
|
+
const sessions = api$1(this.owner.ctx, "sessions", ["flush"]);
|
|
447
|
+
const inspector = api$1(this.owner.ctx, "sessionController", ["inspect"]);
|
|
448
|
+
const presets = api$1(this.owner.ctx, "agentPresets", [
|
|
449
|
+
"resolve",
|
|
450
|
+
"mount",
|
|
451
|
+
"composedPreset",
|
|
452
|
+
"standingKeyFor"
|
|
453
|
+
]);
|
|
454
|
+
const llm = api$1(this.owner.ctx, "llm", ["resolveCallConfig"]);
|
|
455
|
+
const tools = api$1(this.owner.ctx, "tools", [
|
|
456
|
+
"register",
|
|
457
|
+
"restrict",
|
|
458
|
+
"guard",
|
|
459
|
+
"schemas",
|
|
460
|
+
"presentAs"
|
|
461
|
+
]);
|
|
462
|
+
const subagents = api$1(this.owner.ctx, "subagents", [
|
|
463
|
+
"getProvider",
|
|
464
|
+
"startContinuable",
|
|
465
|
+
"sendMessage",
|
|
466
|
+
"listChildren",
|
|
467
|
+
"drainContinuableDescendants"
|
|
468
|
+
]);
|
|
469
|
+
const preview = api$1(this.owner.ctx, "githubCopilotPreview", ["getView", "discover"]);
|
|
470
|
+
if (!agents || !workspaces || !settings || !projections || !persistence || !sessions || !inspector || !presets || !llm || !tools || !subagents || !preview) return void 0;
|
|
471
|
+
const provider = subagents.getProvider("spawn");
|
|
472
|
+
if (!provider || typeof provider.prepareContinuable !== "function" || !provider.capabilities.agentOptions || !provider.capabilities.persona || !provider.capabilities.toolFilter || !provider.capabilities.depthLimit) return void 0;
|
|
303
473
|
return {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
474
|
+
agents,
|
|
475
|
+
workspaces,
|
|
476
|
+
settings,
|
|
477
|
+
projections,
|
|
478
|
+
persistence,
|
|
479
|
+
sessions,
|
|
480
|
+
inspector,
|
|
481
|
+
presets,
|
|
482
|
+
llm,
|
|
483
|
+
tools,
|
|
484
|
+
subagents,
|
|
485
|
+
preview
|
|
315
486
|
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (typeof dshScope.createScope !== "function" || this.lifetime.signal.aborted || this.registrationFailed || !this.settingsReady || !this.projectionReady) return void 0;
|
|
343
|
-
const agents = api$1(this.owner.ctx, "agents", [
|
|
344
|
-
"list",
|
|
345
|
-
"get",
|
|
346
|
-
"create",
|
|
347
|
-
"resume"
|
|
348
|
-
]);
|
|
349
|
-
const workspaces = api$1(this.owner.ctx, "workspaceRegistry", ["get", "list"]);
|
|
350
|
-
const settings = api$1(this.owner.ctx, "settings", [
|
|
351
|
-
"register",
|
|
352
|
-
"describe",
|
|
353
|
-
"replace"
|
|
354
|
-
]);
|
|
355
|
-
const projections = api$1(this.owner.ctx, "sessionProjections", ["register", "stateOf"]);
|
|
356
|
-
const persistence = api$1(this.owner.ctx, "sessionPersistence", ["stat"]);
|
|
357
|
-
const sessions = api$1(this.owner.ctx, "sessions", ["flush"]);
|
|
358
|
-
const inspector = api$1(this.owner.ctx, "sessionController", ["inspect"]);
|
|
359
|
-
const presets = api$1(this.owner.ctx, "agentPresets", [
|
|
360
|
-
"resolve",
|
|
361
|
-
"mount",
|
|
362
|
-
"composedPreset",
|
|
363
|
-
"standingKeyFor"
|
|
364
|
-
]);
|
|
365
|
-
const llm = api$1(this.owner.ctx, "llm", ["resolveCallConfig"]);
|
|
366
|
-
const tools = api$1(this.owner.ctx, "tools", [
|
|
367
|
-
"register",
|
|
368
|
-
"restrict",
|
|
369
|
-
"guard",
|
|
370
|
-
"schemas",
|
|
371
|
-
"presentAs"
|
|
372
|
-
]);
|
|
373
|
-
const subagents = api$1(this.owner.ctx, "subagents", [
|
|
374
|
-
"getProvider",
|
|
375
|
-
"startContinuable",
|
|
376
|
-
"sendMessage",
|
|
377
|
-
"listChildren",
|
|
378
|
-
"drainContinuableDescendants"
|
|
379
|
-
]);
|
|
380
|
-
const preview = api$1(this.owner.ctx, "githubCopilotPreview", ["getView", "discover"]);
|
|
381
|
-
if (!agents || !workspaces || !settings || !projections || !persistence || !sessions || !inspector || !presets || !llm || !tools || !subagents || !preview) return void 0;
|
|
382
|
-
const provider = subagents.getProvider("spawn");
|
|
383
|
-
if (!provider || typeof provider.prepareContinuable !== "function" || !provider.capabilities.agentOptions || !provider.capabilities.persona || !provider.capabilities.toolFilter || !provider.capabilities.depthLimit) return void 0;
|
|
384
|
-
return {
|
|
385
|
-
agents,
|
|
386
|
-
workspaces,
|
|
387
|
-
settings,
|
|
388
|
-
projections,
|
|
389
|
-
persistence,
|
|
390
|
-
sessions,
|
|
391
|
-
inspector,
|
|
392
|
-
presets,
|
|
393
|
-
llm,
|
|
394
|
-
tools,
|
|
395
|
-
subagents,
|
|
396
|
-
preview
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
requireCapabilities() {
|
|
400
|
-
return this.capabilities() ?? fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
401
|
-
}
|
|
402
|
-
configuration() {
|
|
403
|
-
const descriptor = api$1(this.owner.ctx, "settings", ["describe"])?.describe({ redactSecrets: true }).find((value) => value.ns === DUAL_MODEL_NAMESPACE);
|
|
404
|
-
if (!descriptor) return {
|
|
405
|
-
configuration: DEFAULT_CONFIG,
|
|
406
|
-
revision: null
|
|
407
|
-
};
|
|
408
|
-
const parsed = ConfigJson.safeParse(descriptor.value);
|
|
409
|
-
if (!parsed.success || !revision(descriptor.revision)) return fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
410
|
-
return {
|
|
411
|
-
configuration: parsed.data,
|
|
412
|
-
revision: descriptor.revision
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
async view() {
|
|
416
|
-
const empty = {
|
|
417
|
-
supported: false,
|
|
418
|
-
diagnostic: "DUAL_MODEL_UNSUPPORTED",
|
|
419
|
-
writable: false,
|
|
420
|
-
revision: null,
|
|
421
|
-
configuration: DEFAULT_CONFIG,
|
|
422
|
-
models: [],
|
|
423
|
-
workspaces: []
|
|
424
|
-
};
|
|
425
|
-
try {
|
|
426
|
-
const configuration = this.configuration(), cap = this.capabilities();
|
|
427
|
-
if (!cap) return {
|
|
428
|
-
...empty,
|
|
429
|
-
...configuration
|
|
487
|
+
}
|
|
488
|
+
requireCapabilities() {
|
|
489
|
+
return this.capabilities() ?? fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
490
|
+
}
|
|
491
|
+
configuration() {
|
|
492
|
+
const descriptor = api$1(this.owner.ctx, "settings", ["describe"])?.describe({ redactSecrets: true }).find((value) => value.ns === DUAL_MODEL_NAMESPACE);
|
|
493
|
+
if (!descriptor) return {
|
|
494
|
+
configuration: DEFAULT_CONFIG,
|
|
495
|
+
revision: null
|
|
496
|
+
};
|
|
497
|
+
const parsed = ConfigJson.safeParse(descriptor.value);
|
|
498
|
+
if (!parsed.success || !revision(descriptor.revision)) return fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
499
|
+
return {
|
|
500
|
+
configuration: parsed.data,
|
|
501
|
+
revision: descriptor.revision
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
async view() {
|
|
505
|
+
const empty = {
|
|
506
|
+
supported: false,
|
|
507
|
+
diagnostic: "DUAL_MODEL_UNSUPPORTED",
|
|
508
|
+
writable: false,
|
|
509
|
+
revision: null,
|
|
510
|
+
configuration: DEFAULT_CONFIG,
|
|
511
|
+
models: [],
|
|
512
|
+
workspaces: []
|
|
430
513
|
};
|
|
431
|
-
let models = [], diagnostic;
|
|
432
514
|
try {
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
515
|
+
const configuration = this.configuration(), cap = this.capabilities();
|
|
516
|
+
if (!cap) return {
|
|
517
|
+
...empty,
|
|
518
|
+
...configuration
|
|
519
|
+
};
|
|
520
|
+
let models = [], diagnostic;
|
|
521
|
+
try {
|
|
522
|
+
const preview = await cap.preview.discover({
|
|
523
|
+
force: false,
|
|
524
|
+
signal: this.lifetime.signal
|
|
525
|
+
});
|
|
526
|
+
if (preview.available && preview.state === "ready") models = z$1.array(CatalogEntryJson).max(512).parse(preview.models.map(({ id, name }) => ({
|
|
527
|
+
id,
|
|
528
|
+
name
|
|
529
|
+
})));
|
|
530
|
+
else diagnostic = "DUAL_MODEL_MODEL_UNAVAILABLE";
|
|
531
|
+
} catch {
|
|
532
|
+
diagnostic = "DUAL_MODEL_MODEL_UNAVAILABLE";
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
supported: true,
|
|
536
|
+
...diagnostic === void 0 ? {} : { diagnostic },
|
|
537
|
+
writable: cap.settings.writable === true,
|
|
538
|
+
...configuration,
|
|
539
|
+
models,
|
|
540
|
+
workspaces: z$1.array(CatalogEntryJson).max(1024).parse(cap.workspaces.list().map((workspace) => ({
|
|
541
|
+
id: workspace.id,
|
|
542
|
+
name: workspace.title
|
|
543
|
+
})))
|
|
544
|
+
};
|
|
442
545
|
} catch {
|
|
443
|
-
|
|
546
|
+
return empty;
|
|
444
547
|
}
|
|
445
|
-
return {
|
|
446
|
-
supported: true,
|
|
447
|
-
...diagnostic === void 0 ? {} : { diagnostic },
|
|
448
|
-
writable: cap.settings.writable === true,
|
|
449
|
-
...configuration,
|
|
450
|
-
models,
|
|
451
|
-
workspaces: z$1.array(CatalogEntryJson).max(1024).parse(cap.workspaces.list().map((workspace) => ({
|
|
452
|
-
id: workspace.id,
|
|
453
|
-
name: workspace.title
|
|
454
|
-
})))
|
|
455
|
-
};
|
|
456
|
-
} catch {
|
|
457
|
-
return empty;
|
|
458
548
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
549
|
+
async save(input) {
|
|
550
|
+
try {
|
|
551
|
+
return await this.saveOnce(input);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
fail$1(reasonOf(error, "DUAL_MODEL_SAVE_FAILED"));
|
|
554
|
+
}
|
|
465
555
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
556
|
+
async saveOnce(input) {
|
|
557
|
+
const parsed = SaveJson.safeParse(input);
|
|
558
|
+
if (!parsed.success) fail$1("DUAL_MODEL_INVALID_REQUEST");
|
|
559
|
+
const cap = this.requireCapabilities(), { configuration, expectedRevision } = parsed.data;
|
|
560
|
+
if (cap.settings.writable !== true) fail$1("DUAL_MODEL_READ_ONLY");
|
|
561
|
+
if (this.configuration().revision !== expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
|
|
562
|
+
if (configuration.enabled) await this.assertModels(cap, [configuration.plannerModel, configuration.executorModel]);
|
|
563
|
+
try {
|
|
564
|
+
await cap.settings.replace(DUAL_MODEL_NAMESPACE, configuration, expectedRevision);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
fail$1(reasonOf(error, "DUAL_MODEL_SAVE_FAILED"));
|
|
567
|
+
}
|
|
568
|
+
return this.view();
|
|
478
569
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
promise
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
570
|
+
create(input) {
|
|
571
|
+
const parsed = CreateJson.safeParse(input);
|
|
572
|
+
if (!parsed.success) return Promise.reject(new RemoteError("copilot/dual-model", "DUAL_MODEL_INVALID_REQUEST", { reason: "DUAL_MODEL_INVALID_REQUEST" }));
|
|
573
|
+
const request = parsed.data, id = dualModelSessionId(request.requestId), key = requestKey(request);
|
|
574
|
+
const pending = this.creates.get(id);
|
|
575
|
+
if (pending) return pending.key === key ? pending.promise : Promise.reject(new RemoteError("copilot/dual-model", "DUAL_MODEL_REQUEST_CONFLICT", { reason: "DUAL_MODEL_REQUEST_CONFLICT" }));
|
|
576
|
+
const promise = this.createOnce(request, id).catch((error) => fail$1(reasonOf(error, "DUAL_MODEL_CREATE_UNCERTAIN"), creationOf(error)));
|
|
577
|
+
this.creates.set(id, {
|
|
578
|
+
key,
|
|
579
|
+
promise
|
|
580
|
+
});
|
|
581
|
+
promise.then(() => this.creates.delete(id), () => this.creates.delete(id));
|
|
582
|
+
return promise;
|
|
583
|
+
}
|
|
584
|
+
async createOnce(request, id) {
|
|
585
|
+
let creation = "uncertain";
|
|
586
|
+
try {
|
|
587
|
+
const cap = this.requireCapabilities();
|
|
588
|
+
if (await cap.persistence.stat(id) !== void 0 || cap.agents.get(id)) {
|
|
589
|
+
const inspection = await cap.inspector.inspect(id);
|
|
590
|
+
let state = dualModelProjection.init(inspection.meta, inspection.inheritedEventCount);
|
|
591
|
+
for (const event of inspection.events) state = dualModelProjection.apply(state, event);
|
|
592
|
+
if (state.invalid || !state.policy || !policyMatches(state.policy, request)) fail$1("DUAL_MODEL_REQUEST_CONFLICT");
|
|
593
|
+
const workspace = this.workspace(cap, request.workspaceId);
|
|
594
|
+
if (workspace.path !== state.policy.cwd) fail$1("DUAL_MODEL_REQUEST_CONFLICT");
|
|
595
|
+
await workspace.attachSession(id);
|
|
596
|
+
return { sessionId: id };
|
|
597
|
+
}
|
|
598
|
+
creation = "not-created";
|
|
599
|
+
const current = this.configuration();
|
|
600
|
+
if (current.revision !== request.expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
|
|
601
|
+
if (!current.configuration.enabled) fail$1("DUAL_MODEL_DISABLED");
|
|
504
602
|
const workspace = this.workspace(cap, request.workspaceId);
|
|
505
|
-
|
|
506
|
-
await
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (this.configuration().revision !== request.expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
|
|
522
|
-
if (cap.workspaces.get(workspace.id) !== workspace) fail$1("DUAL_MODEL_WORKSPACE_UNAVAILABLE");
|
|
523
|
-
const policy = {
|
|
524
|
-
version: 1,
|
|
525
|
-
rootSessionId: id,
|
|
526
|
-
requestId: request.requestId,
|
|
527
|
-
workspaceId: workspace.id,
|
|
528
|
-
settingsRevision: request.expectedRevision,
|
|
529
|
-
cwd: workspace.path,
|
|
530
|
-
agentPreset: preset.id,
|
|
531
|
-
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
532
|
-
plannerModel: captured.plannerModel,
|
|
533
|
-
executorModel: captured.executorModel,
|
|
534
|
-
plannerTools: [...ROOT_TOOLS],
|
|
535
|
-
executorTools: [...executorTools]
|
|
536
|
-
};
|
|
537
|
-
creation = "uncertain";
|
|
538
|
-
const handle = await cap.agents.create({
|
|
539
|
-
sessionId: id,
|
|
540
|
-
agentOptions: {
|
|
541
|
-
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
542
|
-
model: policy.plannerModel
|
|
543
|
-
},
|
|
544
|
-
meta: {
|
|
545
|
-
cwd: policy.cwd,
|
|
603
|
+
const captured = { ...current.configuration };
|
|
604
|
+
await this.assertModels(cap, [captured.plannerModel, captured.executorModel]);
|
|
605
|
+
const preset = await cap.presets.resolve();
|
|
606
|
+
const standingKey = await cap.presets.standingKeyFor(preset.id);
|
|
607
|
+
const inheritedNames = new Set(cap.tools.schemas(standingKey).map((tool) => tool.name));
|
|
608
|
+
const executorTools = EXECUTOR_TOOLS.filter((name) => inheritedNames.has(name));
|
|
609
|
+
if (!executorTools.includes("send_message")) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
610
|
+
if (this.configuration().revision !== request.expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
|
|
611
|
+
if (cap.workspaces.get(workspace.id) !== workspace) fail$1("DUAL_MODEL_WORKSPACE_UNAVAILABLE");
|
|
612
|
+
const policy = {
|
|
613
|
+
version: 1,
|
|
614
|
+
rootSessionId: id,
|
|
615
|
+
requestId: request.requestId,
|
|
616
|
+
workspaceId: workspace.id,
|
|
617
|
+
settingsRevision: request.expectedRevision,
|
|
618
|
+
cwd: workspace.path,
|
|
546
619
|
agentPreset: preset.id,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
type: "model/selection",
|
|
558
|
-
seq: 1,
|
|
559
|
-
time: Date.now(),
|
|
560
|
-
data: {
|
|
620
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
621
|
+
plannerModel: captured.plannerModel,
|
|
622
|
+
executorModel: captured.executorModel,
|
|
623
|
+
plannerTools: [...ROOT_TOOLS],
|
|
624
|
+
executorTools: [...executorTools]
|
|
625
|
+
};
|
|
626
|
+
creation = "uncertain";
|
|
627
|
+
const handle = await cap.agents.create({
|
|
628
|
+
sessionId: id,
|
|
629
|
+
agentOptions: {
|
|
561
630
|
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
562
631
|
model: policy.plannerModel
|
|
632
|
+
},
|
|
633
|
+
meta: {
|
|
634
|
+
cwd: policy.cwd,
|
|
635
|
+
agentPreset: preset.id,
|
|
636
|
+
isSeeded: false
|
|
637
|
+
},
|
|
638
|
+
inheritedEventCount: 0,
|
|
639
|
+
seed: [{
|
|
640
|
+
type: DUAL_MODEL_POLICY_EVENT,
|
|
641
|
+
seq: 0,
|
|
642
|
+
time: Date.now(),
|
|
643
|
+
ignorable: true,
|
|
644
|
+
data: policy
|
|
645
|
+
}, {
|
|
646
|
+
type: "model/selection",
|
|
647
|
+
seq: 1,
|
|
648
|
+
time: Date.now(),
|
|
649
|
+
data: {
|
|
650
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
651
|
+
model: policy.plannerModel
|
|
652
|
+
}
|
|
653
|
+
}],
|
|
654
|
+
signal: this.lifetime.signal,
|
|
655
|
+
setup: async (agentCtx, agent) => {
|
|
656
|
+
await cap.presets.mount(agentCtx, preset.id);
|
|
657
|
+
this.install(agent, policy, "planner");
|
|
563
658
|
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
this.handles.set(id, handle);
|
|
572
|
-
if (!await cap.sessions.flush(handle.agent.session)) fail$1("DUAL_MODEL_CREATE_UNCERTAIN");
|
|
573
|
-
await workspace.attachSession(id);
|
|
574
|
-
return { sessionId: id };
|
|
575
|
-
} catch (error) {
|
|
576
|
-
fail$1(reasonOf(error, "DUAL_MODEL_CREATE_UNCERTAIN"), creation);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
workspace(cap, id) {
|
|
580
|
-
const workspace = cap.workspaces.get(id);
|
|
581
|
-
if (!workspace || typeof workspace.path !== "string" || typeof workspace.attachSession !== "function") fail$1("DUAL_MODEL_WORKSPACE_UNAVAILABLE");
|
|
582
|
-
return workspace;
|
|
583
|
-
}
|
|
584
|
-
async assertModels(cap, models, signal = this.lifetime.signal) {
|
|
585
|
-
try {
|
|
586
|
-
signal.throwIfAborted();
|
|
587
|
-
const preview = await cap.preview.discover({
|
|
588
|
-
force: false,
|
|
589
|
-
signal
|
|
590
|
-
});
|
|
591
|
-
if (preview.provider !== "github-copilot-preview" || !preview.available || preview.state !== "ready") fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
592
|
-
for (const model of models) {
|
|
593
|
-
if (!model || !preview.models.some((item) => item.id === model)) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
594
|
-
const resolved = await cap.llm.resolveCallConfig({
|
|
595
|
-
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
596
|
-
model
|
|
597
|
-
}, signal);
|
|
598
|
-
if (resolved.provider !== "github-copilot-preview" || resolved.model !== model) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
659
|
+
});
|
|
660
|
+
this.handles.set(id, handle);
|
|
661
|
+
if (!await cap.sessions.flush(handle.agent.session)) fail$1("DUAL_MODEL_CREATE_UNCERTAIN");
|
|
662
|
+
await workspace.attachSession(id);
|
|
663
|
+
return { sessionId: id };
|
|
664
|
+
} catch (error) {
|
|
665
|
+
fail$1(reasonOf(error, "DUAL_MODEL_CREATE_UNCERTAIN"), creation);
|
|
599
666
|
}
|
|
600
|
-
const now = cap.preview.getView();
|
|
601
|
-
if (!now.available || now.state !== "ready" || models.some((model) => !now.models.some((item) => item.id === model))) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
602
|
-
signal.throwIfAborted();
|
|
603
|
-
} catch {
|
|
604
|
-
fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
projection(agent) {
|
|
608
|
-
const projections = api$1(this.owner.ctx, "sessionProjections", ["stateOf"]);
|
|
609
|
-
const parsed = ProjectionJson.safeParse(projections?.stateOf(agent.session, DUAL_MODEL_PROJECTION));
|
|
610
|
-
return parsed.success ? parsed.data : void 0;
|
|
611
|
-
}
|
|
612
|
-
restore(agent) {
|
|
613
|
-
if (this.overlays.has(agent)) return;
|
|
614
|
-
const state = this.projection(agent);
|
|
615
|
-
if (!state) {
|
|
616
|
-
if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
617
|
-
return;
|
|
618
667
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return;
|
|
668
|
+
workspace(cap, id) {
|
|
669
|
+
const workspace = cap.workspaces.get(id);
|
|
670
|
+
if (!workspace || typeof workspace.path !== "string" || typeof workspace.attachSession !== "function") fail$1("DUAL_MODEL_WORKSPACE_UNAVAILABLE");
|
|
671
|
+
return workspace;
|
|
623
672
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
673
|
+
async assertModels(cap, models, signal = this.lifetime.signal) {
|
|
674
|
+
try {
|
|
675
|
+
signal.throwIfAborted();
|
|
676
|
+
const preview = await cap.preview.discover({
|
|
677
|
+
force: false,
|
|
678
|
+
signal
|
|
679
|
+
});
|
|
680
|
+
if (preview.provider !== "github-copilot-preview" || !preview.available || preview.state !== "ready") fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
681
|
+
for (const model of models) {
|
|
682
|
+
if (!model || !preview.models.some((item) => item.id === model)) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
683
|
+
const resolved = await cap.llm.resolveCallConfig({
|
|
684
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
685
|
+
model
|
|
686
|
+
}, signal);
|
|
687
|
+
if (resolved.provider !== "github-copilot-preview" || resolved.model !== model) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
688
|
+
}
|
|
689
|
+
const now = cap.preview.getView();
|
|
690
|
+
if (!now.available || now.state !== "ready" || models.some((model) => !now.models.some((item) => item.id === model))) fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
691
|
+
signal.throwIfAborted();
|
|
692
|
+
} catch {
|
|
693
|
+
fail$1("DUAL_MODEL_MODEL_UNAVAILABLE");
|
|
694
|
+
}
|
|
627
695
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
return;
|
|
696
|
+
projection(agent) {
|
|
697
|
+
const projections = api$1(this.owner.ctx, "sessionProjections", ["stateOf"]);
|
|
698
|
+
const parsed = ProjectionJson.safeParse(projections?.stateOf(agent.session, DUAL_MODEL_PROJECTION));
|
|
699
|
+
return parsed.success ? parsed.data : void 0;
|
|
632
700
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
if (
|
|
637
|
-
|
|
701
|
+
restore(agent) {
|
|
702
|
+
if (this.overlays.has(agent)) return;
|
|
703
|
+
const state = this.projection(agent);
|
|
704
|
+
if (!state) {
|
|
705
|
+
if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (state.invalid) fail$1("DUAL_MODEL_POLICY_INVALID");
|
|
709
|
+
if (state.policy) {
|
|
710
|
+
this.install(agent, state.policy, "planner");
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (state.origin !== "subagent" || state.parentId === null) {
|
|
714
|
+
if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_POLICY_INVALID");
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const parent = api$1(this.owner.ctx, "agents", ["get"])?.get(state.parentId);
|
|
718
|
+
if (!parent) {
|
|
719
|
+
if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (this.overlays.get(parent)?.role === "executor") fail$1("DUAL_MODEL_DELEGATION_DENIED");
|
|
723
|
+
const parentPolicy = this.projection(parent)?.policy;
|
|
724
|
+
if (!parentPolicy) {
|
|
725
|
+
if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_POLICY_INVALID");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (!state.child || state.child.agentProvider !== "github-copilot-preview" || state.child.agentModel !== parentPolicy.executorModel) fail$1("DUAL_MODEL_POLICY_INVALID");
|
|
729
|
+
this.install(agent, parentPolicy, "executor");
|
|
638
730
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
"
|
|
653
|
-
"
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
name: DUAL_MODEL_EXECUTE_TOOL,
|
|
676
|
-
description: `Delegate implementation to a continuable executor fixed to ${GITHUB_COPILOT_PREVIEW_PROVIDER_ID}/${policy.executorModel}. Returns a durable child ID, not completion. Continue with send_message and inspect with list_agents. No model override or recursive delegation is allowed.`,
|
|
677
|
-
parameters: {
|
|
678
|
-
type: "object",
|
|
679
|
-
additionalProperties: false,
|
|
680
|
-
properties: {
|
|
681
|
-
description: { type: "string" },
|
|
682
|
-
prompt: { type: "string" }
|
|
683
|
-
},
|
|
684
|
-
required: ["description", "prompt"]
|
|
685
|
-
},
|
|
686
|
-
output: {
|
|
687
|
-
schema: {
|
|
731
|
+
install(agent, policy, role) {
|
|
732
|
+
if (this.overlays.has(agent)) return;
|
|
733
|
+
this.requireCapabilities();
|
|
734
|
+
if (typeof agent.cancel !== "function" || typeof agent.whenIdle !== "function") fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
735
|
+
const overlay = dshScope.createScope(agent.ctx, agent), scoped = overlay.ctx;
|
|
736
|
+
try {
|
|
737
|
+
const tools = api$1(scoped, "tools", [
|
|
738
|
+
"schemas",
|
|
739
|
+
"register",
|
|
740
|
+
"restrict",
|
|
741
|
+
"guard",
|
|
742
|
+
"presentAs"
|
|
743
|
+
]);
|
|
744
|
+
const prompt = api$1(scoped, "systemPrompt", ["section"]);
|
|
745
|
+
if (!tools || !prompt) fail$1("DUAL_MODEL_UNSUPPORTED");
|
|
746
|
+
const allowed = new Set(role === "planner" ? policy.plannerTools : policy.executorTools);
|
|
747
|
+
const available = tools.schemas(agent).map((item) => item.name);
|
|
748
|
+
tools.guard((exec) => allowed.has(exec.name) ? void 0 : "DUAL_MODEL_TOOL_DENIED");
|
|
749
|
+
tools.presentAs("native");
|
|
750
|
+
tools.restrict({ allow: available.filter((name) => allowed.has(name) && name !== "copilot_execute") });
|
|
751
|
+
const selectedModel = role === "planner" ? policy.plannerModel : policy.executorModel;
|
|
752
|
+
prompt.section({
|
|
753
|
+
name: "github-copilot:dual-model-role",
|
|
754
|
+
order: 1e3,
|
|
755
|
+
text: roleText(policy, role)
|
|
756
|
+
});
|
|
757
|
+
scoped.on("agent/request", async ({ signal }, next) => {
|
|
758
|
+
await this.assertModels(this.requireCapabilities(), [selectedModel], signal);
|
|
759
|
+
const resolved = await next();
|
|
760
|
+
if (resolved.provider !== "github-copilot-preview" || resolved.model !== selectedModel) fail$1("DUAL_MODEL_SELECTION_LOCKED");
|
|
761
|
+
return resolved;
|
|
762
|
+
}, { prepend: true });
|
|
763
|
+
if (role === "planner") tools.register({
|
|
764
|
+
name: DUAL_MODEL_EXECUTE_TOOL,
|
|
765
|
+
description: `Delegate implementation to a continuable executor fixed to ${GITHUB_COPILOT_PREVIEW_PROVIDER_ID}/${policy.executorModel}. Returns a durable child ID, not completion. Continue with send_message and inspect with list_agents. No model override or recursive delegation is allowed.`,
|
|
766
|
+
parameters: {
|
|
688
767
|
type: "object",
|
|
689
768
|
additionalProperties: false,
|
|
690
769
|
properties: {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
model: { type: "string" }
|
|
770
|
+
description: { type: "string" },
|
|
771
|
+
prompt: { type: "string" }
|
|
694
772
|
},
|
|
695
|
-
required: [
|
|
696
|
-
"childId",
|
|
697
|
-
"provider",
|
|
698
|
-
"model"
|
|
699
|
-
]
|
|
773
|
+
required: ["description", "prompt"]
|
|
700
774
|
},
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
775
|
+
output: {
|
|
776
|
+
schema: {
|
|
777
|
+
type: "object",
|
|
778
|
+
additionalProperties: false,
|
|
779
|
+
properties: {
|
|
780
|
+
childId: { type: "string" },
|
|
781
|
+
provider: { type: "string" },
|
|
782
|
+
model: { type: "string" }
|
|
783
|
+
},
|
|
784
|
+
required: [
|
|
785
|
+
"childId",
|
|
786
|
+
"provider",
|
|
787
|
+
"model"
|
|
788
|
+
]
|
|
789
|
+
},
|
|
790
|
+
render: (_args, value) => [{
|
|
791
|
+
type: "text",
|
|
792
|
+
text: JSON.stringify(value)
|
|
793
|
+
}]
|
|
794
|
+
},
|
|
795
|
+
execute: async (input, exec) => {
|
|
796
|
+
try {
|
|
797
|
+
const parsed = ExecuteJson.safeParse(input);
|
|
798
|
+
if (!parsed.success || exec.agent !== agent) fail$1("DUAL_MODEL_INVALID_REQUEST");
|
|
799
|
+
const current = this.requireCapabilities();
|
|
800
|
+
await this.assertModels(current, [policy.executorModel], exec.signal);
|
|
801
|
+
return {
|
|
802
|
+
childId: (await current.subagents.startContinuable({
|
|
803
|
+
provider: "spawn",
|
|
804
|
+
label: parsed.data.description,
|
|
805
|
+
request: {
|
|
806
|
+
parent: agent,
|
|
807
|
+
prompt: [{
|
|
808
|
+
type: "text",
|
|
809
|
+
text: parsed.data.prompt
|
|
810
|
+
}],
|
|
811
|
+
agentOptions: {
|
|
812
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
813
|
+
model: policy.executorModel
|
|
814
|
+
},
|
|
815
|
+
persona: EXECUTOR_PERSONA,
|
|
816
|
+
toolFilter: { allow: policy.executorTools },
|
|
817
|
+
maxDepth: 1
|
|
725
818
|
},
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
};
|
|
735
|
-
} catch (error) {
|
|
736
|
-
fail$1(reasonOf(error, "DUAL_MODEL_EXECUTION_FAILED"));
|
|
819
|
+
signal: exec.signal
|
|
820
|
+
})).childId,
|
|
821
|
+
provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
|
|
822
|
+
model: policy.executorModel
|
|
823
|
+
};
|
|
824
|
+
} catch (error) {
|
|
825
|
+
fail$1(reasonOf(error, "DUAL_MODEL_EXECUTION_FAILED"));
|
|
826
|
+
}
|
|
737
827
|
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
})
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
828
|
+
});
|
|
829
|
+
this.overlays.set(agent, {
|
|
830
|
+
dispose: overlay.dispose,
|
|
831
|
+
policy,
|
|
832
|
+
role
|
|
833
|
+
});
|
|
834
|
+
} catch (error) {
|
|
835
|
+
overlay.dispose().catch(() => {});
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
748
838
|
}
|
|
839
|
+
};
|
|
840
|
+
})();
|
|
841
|
+
//#endregion
|
|
842
|
+
//#region lib/types/search-routing-remote.js
|
|
843
|
+
const SearchProviderCatalogSchema = z$1.object({
|
|
844
|
+
supported: z$1.boolean(),
|
|
845
|
+
providers: z$1.array(z$1.object({ id: z$1.string().min(1).max(512) }).strict()).max(512)
|
|
846
|
+
}).strict();
|
|
847
|
+
//#endregion
|
|
848
|
+
//#region lib/types/search-routing-host.js
|
|
849
|
+
var __runInitializers$1 = function(thisArg, initializers, value) {
|
|
850
|
+
var useValue = arguments.length > 2;
|
|
851
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
852
|
+
return useValue ? value : void 0;
|
|
853
|
+
};
|
|
854
|
+
var __esDecorate$1 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
855
|
+
function accept(f) {
|
|
856
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
857
|
+
return f;
|
|
749
858
|
}
|
|
859
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
860
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
861
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
862
|
+
var _, done = false;
|
|
863
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
864
|
+
var context = {};
|
|
865
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
866
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
867
|
+
context.addInitializer = function(f) {
|
|
868
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
869
|
+
extraInitializers.push(accept(f || null));
|
|
870
|
+
};
|
|
871
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
872
|
+
get: descriptor.get,
|
|
873
|
+
set: descriptor.set
|
|
874
|
+
} : descriptor[key], context);
|
|
875
|
+
if (kind === "accessor") {
|
|
876
|
+
if (result === void 0) continue;
|
|
877
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
878
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
879
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
880
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
881
|
+
} else if (_ = accept(result)) {
|
|
882
|
+
if (kind === "field") initializers.unshift(_);
|
|
883
|
+
else descriptor[key] = _;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
887
|
+
done = true;
|
|
750
888
|
};
|
|
889
|
+
let SearchRoutingController = (() => {
|
|
890
|
+
let _classSuper = TypertRemoteService;
|
|
891
|
+
let _instanceExtraInitializers = [];
|
|
892
|
+
let _providers_decorators;
|
|
893
|
+
return class SearchRoutingController extends _classSuper {
|
|
894
|
+
static {
|
|
895
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
896
|
+
_providers_decorators = [Remote];
|
|
897
|
+
__esDecorate$1(this, null, _providers_decorators, {
|
|
898
|
+
kind: "method",
|
|
899
|
+
name: "providers",
|
|
900
|
+
static: false,
|
|
901
|
+
private: false,
|
|
902
|
+
access: {
|
|
903
|
+
has: (obj) => "providers" in obj,
|
|
904
|
+
get: (obj) => obj.providers
|
|
905
|
+
},
|
|
906
|
+
metadata: _metadata
|
|
907
|
+
}, null, _instanceExtraInitializers);
|
|
908
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
909
|
+
enumerable: true,
|
|
910
|
+
configurable: true,
|
|
911
|
+
writable: true,
|
|
912
|
+
value: _metadata
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
constructor(ctx) {
|
|
916
|
+
super(ctx, "githubCopilotSearchRouting");
|
|
917
|
+
__runInitializers$1(this, _instanceExtraInitializers);
|
|
918
|
+
}
|
|
919
|
+
providers() {
|
|
920
|
+
const unavailable = {
|
|
921
|
+
supported: false,
|
|
922
|
+
providers: []
|
|
923
|
+
};
|
|
924
|
+
const catalog = this.ctx.get("githubCopilotSearchCatalog");
|
|
925
|
+
if (typeof catalog?.list !== "function") return unavailable;
|
|
926
|
+
try {
|
|
927
|
+
const ids = catalog.list();
|
|
928
|
+
const parsed = SearchProviderCatalogSchema.safeParse({
|
|
929
|
+
supported: true,
|
|
930
|
+
providers: ids.map((id) => ({ id }))
|
|
931
|
+
});
|
|
932
|
+
if (!parsed.success || new Set(ids).size !== ids.length) return unavailable;
|
|
933
|
+
parsed.data.providers.sort((a, b) => a.id.localeCompare(b.id));
|
|
934
|
+
return parsed.data;
|
|
935
|
+
} catch {
|
|
936
|
+
return unavailable;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
})();
|
|
751
941
|
//#endregion
|
|
752
942
|
//#region package.json
|
|
753
943
|
var name$1 = "dsh-github-copilot";
|
|
754
|
-
var version = "0.4.0-alpha.
|
|
944
|
+
var version = "0.4.0-alpha.23";
|
|
755
945
|
//#endregion
|
|
756
946
|
//#region lib/types/probe.js
|
|
757
947
|
/**
|
|
@@ -1079,6 +1269,18 @@ const Config = z.object({
|
|
|
1079
1269
|
temporaryRouteBackup: z.string().hidden()
|
|
1080
1270
|
});
|
|
1081
1271
|
//#endregion
|
|
1272
|
+
//#region lib/types/search-routing-policy.js
|
|
1273
|
+
/** Resolve old and new settings without guessing registered providers or dropping unknown ids. */
|
|
1274
|
+
function normalizeWebSearchRouting(config) {
|
|
1275
|
+
const defaultProvider = config.defaultSearchProvider?.trim() || "deepseek-official";
|
|
1276
|
+
const legacy = config.searchProvider === void 0;
|
|
1277
|
+
return {
|
|
1278
|
+
primaryProvider: legacy ? config.searchMode === "fixed" ? defaultProvider : "auto" : config.searchProvider?.trim() || "auto",
|
|
1279
|
+
defaultProvider,
|
|
1280
|
+
legacy
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
//#endregion
|
|
1082
1284
|
//#region lib/types/web-search-routing-config.js
|
|
1083
1285
|
/** Plugin-owned settings namespace for cross-provider web-search routing. */
|
|
1084
1286
|
const WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE = "github-copilot-search-routing";
|
|
@@ -1086,10 +1288,90 @@ const WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE = "github-copilot-search-routing";
|
|
|
1086
1288
|
const NO_DEFAULT_SEARCH_PROVIDER = "none";
|
|
1087
1289
|
/** User-facing routing settings; provider-specific credentials and models remain provider-owned. */
|
|
1088
1290
|
const WebSearchRoutingConfigSchema = z.object({
|
|
1291
|
+
searchProvider: z.string(),
|
|
1089
1292
|
searchMode: z.union(["auto", "fixed"]).default("auto"),
|
|
1090
1293
|
defaultSearchProvider: z.string().default("deepseek-official")
|
|
1091
1294
|
});
|
|
1092
1295
|
//#endregion
|
|
1296
|
+
//#region lib/types/search-tool-routing.js
|
|
1297
|
+
function aborted() {
|
|
1298
|
+
return new WebError("web search aborted", "WEB_ABORTED");
|
|
1299
|
+
}
|
|
1300
|
+
function isAborted(signal) {
|
|
1301
|
+
return signal?.aborted === true;
|
|
1302
|
+
}
|
|
1303
|
+
function assertContinuable(signal, canContinue) {
|
|
1304
|
+
if (isAborted(signal)) throw aborted();
|
|
1305
|
+
let current = false;
|
|
1306
|
+
try {
|
|
1307
|
+
current = canContinue();
|
|
1308
|
+
} catch {}
|
|
1309
|
+
if (isAborted(signal)) throw aborted();
|
|
1310
|
+
if (!current) throw new WebError("web search owner or account proof invalidated", "WEB_PROVIDER_UNAVAILABLE");
|
|
1311
|
+
}
|
|
1312
|
+
function isCancellation(error) {
|
|
1313
|
+
return error instanceof WebError && error.code === "WEB_ABORTED" || error instanceof Error && error.name === "AbortError";
|
|
1314
|
+
}
|
|
1315
|
+
/** Only shared public codes may leave the provider boundary; messages, causes and custom codes stay private. */
|
|
1316
|
+
function failureCode(error) {
|
|
1317
|
+
if (error instanceof WebError && [
|
|
1318
|
+
"WEB_PROVIDER_CONFIGURED_MISSING",
|
|
1319
|
+
"WEB_PROVIDER_CONFIGURED_UNAVAILABLE",
|
|
1320
|
+
"WEB_PROVIDER_UNAVAILABLE",
|
|
1321
|
+
"WEB_PROVIDER_ERROR",
|
|
1322
|
+
"WEB_PROVIDER_AMBIGUOUS"
|
|
1323
|
+
].includes(error.code)) return error.code;
|
|
1324
|
+
return "WEB_PROVIDER_ERROR";
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Execute a captured primary and optional final fallback without registry or credential access.
|
|
1328
|
+
* The caller resolves Auto, handles disabled policy, proves registrations and supplies lifetime guards.
|
|
1329
|
+
* Generic providers must honor the signal; this helper cannot inspect their internal auth/HTTP boundary.
|
|
1330
|
+
*/
|
|
1331
|
+
async function routeSearchTools(request, signal, deps) {
|
|
1332
|
+
const { primary, fallback, canContinue, allowFailureFallback = true } = deps;
|
|
1333
|
+
const primaryId = primary?.id;
|
|
1334
|
+
const primarySearch = primary?.search.bind(primary);
|
|
1335
|
+
const fallbackId = fallback?.id;
|
|
1336
|
+
const fallbackSearch = fallback?.search.bind(fallback);
|
|
1337
|
+
assertContinuable(signal, canContinue);
|
|
1338
|
+
let primaryFailure;
|
|
1339
|
+
if (primarySearch !== void 0) {
|
|
1340
|
+
let result;
|
|
1341
|
+
try {
|
|
1342
|
+
result = await primarySearch(request, signal);
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
assertContinuable(signal, canContinue);
|
|
1345
|
+
if (isCancellation(error)) throw aborted();
|
|
1346
|
+
primaryFailure = error;
|
|
1347
|
+
if (!allowFailureFallback || fallbackSearch === void 0 || fallbackId === primaryId) throw new WebError(`Search provider ${JSON.stringify(primaryId)} failed (${failureCode(error)}).`, failureCode(error));
|
|
1348
|
+
return runFallback();
|
|
1349
|
+
}
|
|
1350
|
+
assertContinuable(signal, canContinue);
|
|
1351
|
+
return result;
|
|
1352
|
+
}
|
|
1353
|
+
return runFallback();
|
|
1354
|
+
async function runFallback() {
|
|
1355
|
+
assertContinuable(signal, canContinue);
|
|
1356
|
+
if (fallbackSearch === void 0) throw new WebError("no selected search provider is available", "WEB_PROVIDER_UNAVAILABLE");
|
|
1357
|
+
const reason = primarySearch === void 0 ? "no matching primary provider" : failureCode(primaryFailure);
|
|
1358
|
+
const notice = `Search provider: ${JSON.stringify(fallbackId)} (final fallback${primaryId === void 0 ? "" : ` from ${JSON.stringify(primaryId)}`}; reason: ${reason}). This fallback may incur charges from the selected search backend.`;
|
|
1359
|
+
let result;
|
|
1360
|
+
try {
|
|
1361
|
+
result = await fallbackSearch(request, signal);
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
assertContinuable(signal, canContinue);
|
|
1364
|
+
if (isCancellation(error)) throw aborted();
|
|
1365
|
+
throw new WebError(`${notice} The final fallback failed (${failureCode(error)}).`, "WEB_PROVIDER_ERROR");
|
|
1366
|
+
}
|
|
1367
|
+
assertContinuable(signal, canContinue);
|
|
1368
|
+
return {
|
|
1369
|
+
...result,
|
|
1370
|
+
content: `${notice}\n\n${result.content ?? ""}`.trimEnd()
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
//#endregion
|
|
1093
1375
|
//#region lib/types/sse.js
|
|
1094
1376
|
/**
|
|
1095
1377
|
* Bounded SSE line parser for the inline wire path. Handles `event:` and
|
|
@@ -5662,6 +5944,11 @@ const integrationInject = [
|
|
|
5662
5944
|
];
|
|
5663
5945
|
/** Settings namespace carrying this plugin's section. */
|
|
5664
5946
|
const GITHUB_COPILOT_SETTINGS_NAMESPACE = "github-copilot";
|
|
5947
|
+
var ConfiguredSearchProofInvalidated = class extends WebError {
|
|
5948
|
+
constructor() {
|
|
5949
|
+
super("configured Copilot search owner or account proof invalidated", "WEB_PROVIDER_UNAVAILABLE");
|
|
5950
|
+
}
|
|
5951
|
+
};
|
|
5665
5952
|
function isInstanceSettingsInstaller(value) {
|
|
5666
5953
|
return typeof value === "object" && value !== null && "installSection" in value && typeof value.installSection === "function";
|
|
5667
5954
|
}
|
|
@@ -5728,6 +6015,7 @@ function activate(ctx, config) {
|
|
|
5728
6015
|
ctx.plugin(preview_route_default, { accountModelSettings: () => current() });
|
|
5729
6016
|
ctx.plugin(GitHubCopilotAuthorizationController);
|
|
5730
6017
|
ctx.plugin(GitHubCopilotDualModel);
|
|
6018
|
+
ctx.plugin(SearchRoutingController);
|
|
5731
6019
|
const resolveGitHubCopilotToken = createGitHubCopilotTokenResolver(ctx, async () => {
|
|
5732
6020
|
await ensureGitHubCopilotProviderProfile(ctx);
|
|
5733
6021
|
});
|
|
@@ -5927,37 +6215,110 @@ function activate(ctx, config) {
|
|
|
5927
6215
|
const owner = currentSearchInitiator(ctx);
|
|
5928
6216
|
const startedGeneration = generation;
|
|
5929
6217
|
const cfg = current();
|
|
5930
|
-
|
|
5931
|
-
|
|
6218
|
+
const preview = ctx.get("githubCopilotPreview");
|
|
6219
|
+
const proof = preview?.captureSearchProof();
|
|
5932
6220
|
const assertCurrent = () => {
|
|
5933
6221
|
if (signal?.aborted) throw new WebError("web search aborted", "WEB_ABORTED");
|
|
5934
|
-
if (!active || generation !== startedGeneration || owner === void 0 || disposedOwners.has(owner) || preview === void 0 || ctx.get("githubCopilotPreview") !== preview || proof?.() !== true) throw new
|
|
6222
|
+
if (!active || generation !== startedGeneration || owner === void 0 || disposedOwners.has(owner) || preview === void 0 || ctx.get("githubCopilotPreview") !== preview || proof?.() !== true) throw new ConfiguredSearchProofInvalidated();
|
|
5935
6223
|
};
|
|
5936
|
-
|
|
6224
|
+
assertCurrent();
|
|
6225
|
+
const provider = createTraditionalSearchProvider(configuredAvailable, async (requestSignal) => {
|
|
6226
|
+
assertCurrent();
|
|
5937
6227
|
const searchPlan = await configuredWebPlan(requestSignal);
|
|
5938
|
-
preview = ctx.get("githubCopilotPreview");
|
|
5939
|
-
proof = preview?.captureSearchProof();
|
|
5940
6228
|
assertCurrent();
|
|
5941
6229
|
return searchPlan;
|
|
5942
6230
|
}, {
|
|
5943
6231
|
...hooks,
|
|
5944
6232
|
resolveApiKey: async (candidate) => {
|
|
6233
|
+
assertCurrent();
|
|
5945
6234
|
const auth = await hooks.resolveApiKey(candidate);
|
|
5946
|
-
|
|
6235
|
+
assertCurrent();
|
|
5947
6236
|
return auth;
|
|
5948
6237
|
}
|
|
5949
|
-
}, () => cfg, "github-copilot-hosted requires github-copilot.searchModel selecting an account-authorized OpenAI Responses model")
|
|
5950
|
-
|
|
5951
|
-
|
|
6238
|
+
}, () => cfg, "github-copilot-hosted requires github-copilot.searchModel selecting an account-authorized OpenAI Responses model");
|
|
6239
|
+
try {
|
|
6240
|
+
const result = await provider.search(request, signal);
|
|
6241
|
+
assertCurrent();
|
|
6242
|
+
return result;
|
|
6243
|
+
} catch (error) {
|
|
6244
|
+
assertCurrent();
|
|
6245
|
+
throw error;
|
|
6246
|
+
}
|
|
5952
6247
|
}
|
|
5953
6248
|
ctx.web.registerSearchProvider(traditionalProvider);
|
|
5954
|
-
ctx.provide("githubCopilotSearchRouter", { search: async (request, signal, delegate, selectProvider) => {
|
|
6249
|
+
ctx.provide("githubCopilotSearchRouter", { search: async (request, signal, delegate, selectProvider, captureSearchProvider) => {
|
|
5955
6250
|
const cfg = current();
|
|
5956
6251
|
const routing = routingCurrent();
|
|
6252
|
+
const policy = normalizeWebSearchRouting(routing);
|
|
5957
6253
|
const owner = currentSearchInitiator(ctx);
|
|
5958
6254
|
const selection = currentSearchSelection(owner);
|
|
5959
6255
|
const managedOwned = selection?.provider === "github-copilot-preview" && isPluginPreviewProvider(ctx, selection.provider);
|
|
5960
|
-
if (!cfg.enabled || cfg.routeWebSearch === false
|
|
6256
|
+
if (!cfg.enabled || cfg.routeWebSearch === false) return delegate(request, signal);
|
|
6257
|
+
if (!policy.legacy && captureSearchProvider === void 0) throw new WebError("explicit search routing requires captureSearchProvider from the routed Web facade", "WEB_PROVIDER_UNAVAILABLE");
|
|
6258
|
+
if (owner === void 0) {
|
|
6259
|
+
if (!policy.legacy) throw new WebError("explicit search routing requires agents.currentInitiator()", "WEB_PROVIDER_UNAVAILABLE");
|
|
6260
|
+
return delegate(request, signal);
|
|
6261
|
+
}
|
|
6262
|
+
if (captureSearchProvider !== void 0) {
|
|
6263
|
+
if (policy.primaryProvider === "none") throw new WebError("web search is disabled by the primary search provider setting", "WEB_PROVIDER_UNAVAILABLE");
|
|
6264
|
+
const startedGeneration = generation;
|
|
6265
|
+
const registrations = [];
|
|
6266
|
+
let usingManagedPrimary = false;
|
|
6267
|
+
let configuredProofInvalid = false;
|
|
6268
|
+
const managedPreview = managedOwned ? ctx.get("githubCopilotPreview") : void 0;
|
|
6269
|
+
const managedProof = managedPreview?.captureSearchProof();
|
|
6270
|
+
const capture = (id) => {
|
|
6271
|
+
const provider = captureSearchProvider(id);
|
|
6272
|
+
if (provider !== void 0) registrations.push(provider);
|
|
6273
|
+
return provider;
|
|
6274
|
+
};
|
|
6275
|
+
const missing = (id) => ({
|
|
6276
|
+
id,
|
|
6277
|
+
search: async () => {
|
|
6278
|
+
throw new WebError("the configured search provider is not registered", "WEB_PROVIDER_CONFIGURED_MISSING");
|
|
6279
|
+
}
|
|
6280
|
+
});
|
|
6281
|
+
const configured = (id) => {
|
|
6282
|
+
const registration = capture(id);
|
|
6283
|
+
if (registration === void 0) return missing(id);
|
|
6284
|
+
if (id !== "github-copilot-hosted" || !registration.owns(traditionalProvider)) return registration;
|
|
6285
|
+
return {
|
|
6286
|
+
id,
|
|
6287
|
+
search: async (query, querySignal) => {
|
|
6288
|
+
try {
|
|
6289
|
+
return await configuredSearch(query, querySignal);
|
|
6290
|
+
} catch (error) {
|
|
6291
|
+
if (error instanceof ConfiguredSearchProofInvalidated) configuredProofInvalid = true;
|
|
6292
|
+
throw error;
|
|
6293
|
+
}
|
|
6294
|
+
}
|
|
6295
|
+
};
|
|
6296
|
+
};
|
|
6297
|
+
let primary;
|
|
6298
|
+
if (policy.primaryProvider === "auto") {
|
|
6299
|
+
if (isCopilotSearchSelection(selection, managedOwned) && (cfg.providers.length === 0 || cfg.providers.includes(selection?.provider ?? ""))) {
|
|
6300
|
+
const native = capture(GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID);
|
|
6301
|
+
if (native?.owns(traditionalProvider)) {
|
|
6302
|
+
usingManagedPrimary = managedOwned;
|
|
6303
|
+
primary = native;
|
|
6304
|
+
}
|
|
6305
|
+
} else if (selection !== void 0) primary = capture(selection.provider);
|
|
6306
|
+
} else primary = configured(policy.primaryProvider);
|
|
6307
|
+
const fallback = policy.defaultProvider === "none" ? void 0 : configured(policy.defaultProvider);
|
|
6308
|
+
const boundSignal = AbortSignal.any([
|
|
6309
|
+
...signal === void 0 ? [] : [signal],
|
|
6310
|
+
proofCancellation.signal,
|
|
6311
|
+
plansFor(owner).cancellation.signal,
|
|
6312
|
+
...registrations.map((provider) => provider.signal)
|
|
6313
|
+
]);
|
|
6314
|
+
const canContinue = () => active && generation === startedGeneration && !disposedOwners.has(owner) && !configuredProofInvalid && registrations.every((provider) => provider.current()) && (!usingManagedPrimary || ctx.get("githubCopilotPreview") === managedPreview && managedProof?.() === true);
|
|
6315
|
+
return routeSearchTools(request, boundSignal, {
|
|
6316
|
+
primary,
|
|
6317
|
+
fallback,
|
|
6318
|
+
canContinue,
|
|
6319
|
+
allowFailureFallback: !policy.legacy || cfg.searchFallback !== "none"
|
|
6320
|
+
});
|
|
6321
|
+
}
|
|
5961
6322
|
const nativeAllowed = isCopilotSearchSelection(selection, managedOwned) && (cfg.providers.length === 0 || cfg.providers.includes(selection?.provider ?? ""));
|
|
5962
6323
|
const useNative = (routing.searchMode ?? "auto") === "auto" && nativeAllowed && (selectProvider === void 0 || traditionalAvailable());
|
|
5963
6324
|
const defaultProviderId = routing.defaultSearchProvider?.trim() || "deepseek-official";
|
|
@@ -6005,7 +6366,7 @@ function activate(ctx, config) {
|
|
|
6005
6366
|
});
|
|
6006
6367
|
ctx.on("llm/stream", (request, next) => {
|
|
6007
6368
|
const cfg = current();
|
|
6008
|
-
if (!active || routingCurrent().
|
|
6369
|
+
if (!active || normalizeWebSearchRouting(routingCurrent()).primaryProvider !== "auto") return next();
|
|
6009
6370
|
const owner = currentSearchInitiator(ctx);
|
|
6010
6371
|
if (owner !== void 0 && disposedOwners.has(owner)) return next();
|
|
6011
6372
|
const route = currentChatRoute(ctx, request);
|
|
@@ -6024,7 +6385,8 @@ function activate(ctx, config) {
|
|
|
6024
6385
|
text: () => ""
|
|
6025
6386
|
});
|
|
6026
6387
|
return (owner, selection) => {
|
|
6027
|
-
|
|
6388
|
+
const policy = normalizeWebSearchRouting(routingCurrent());
|
|
6389
|
+
if (!active || owner === void 0 || disposedOwners.has(owner) || policy.primaryProvider !== "auto") return "";
|
|
6028
6390
|
const route = currentChatRoute(ctx, selection);
|
|
6029
6391
|
const cfg = current();
|
|
6030
6392
|
if (route === void 0 || cfg.providers.length > 0 && !cfg.providers.includes(route.provider)) return "";
|
|
@@ -6032,7 +6394,7 @@ function activate(ctx, config) {
|
|
|
6032
6394
|
const nativeGuidance = servingPrompt(current, matches(cached, route, candidatesForRoute(route), cfg) ? cached.plan : void 0);
|
|
6033
6395
|
const owned = route.provider === "github-copilot-preview" && isPluginPreviewProvider(ctx, route.provider);
|
|
6034
6396
|
if (!cfg.enabled || cfg.routeWebSearch === false || ctx.get("githubCopilotOriginalWeb") === void 0 || !isCopilotSearchSelection(route, owned)) return nativeGuidance;
|
|
6035
|
-
return [nativeGuidance, cfg.searchFallback === "none" ? "Copilot search fallback is disabled. Do not silently substitute another paid search backend after a search failure." : "When web_search reports a fallback, explicitly tell the user the actual search backend (and custom endpoint/model when provided) and possible API charges in your answer. Never describe fallback results as Copilot search. Automatic fallback does not need per-search confirmation."].filter(Boolean).join("\n\n");
|
|
6397
|
+
return [nativeGuidance, policy.defaultProvider === "none" || policy.legacy && cfg.searchFallback === "none" ? "Copilot search fallback is disabled. Do not silently substitute another paid search backend after a search failure." : "When web_search reports a fallback, explicitly tell the user the actual search backend (and custom endpoint/model when provided) and possible API charges in your answer. Never describe fallback results as Copilot search. Automatic fallback does not need per-search confirmation."].filter(Boolean).join("\n\n");
|
|
6036
6398
|
};
|
|
6037
6399
|
}
|
|
6038
6400
|
/**
|