dsh-github-copilot 0.4.0-alpha.18 → 0.4.0-alpha.20

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/lib/index.js CHANGED
@@ -1,21 +1,757 @@
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
+ import { createHash, randomUUID } from "node:crypto";
4
+ import { Service } from "@deepseek-ai/cordis";
5
+ import * as dshScope from "@deepseek-ai/dsh-scope";
6
+ import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
7
+ import z from "@deepseek-ai/schemastery";
8
+ import { z as z$1 } from "zod";
3
9
  import * as dshLlm from "@deepseek-ai/dsh-llm";
4
10
  import { LlmError, attributionHeaders, contentHasImage, isAgentLoopRequest, resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
5
11
  import { WebError } from "@deepseek-ai/dsh-web";
6
12
  import * as dshSettings from "@deepseek-ai/dsh-settings";
7
13
  import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
8
14
  import { createModels, getSupportedThinkingLevels, hasApi, lazyStream } from "@earendil-works/pi-ai";
9
- import z from "@deepseek-ai/schemastery";
10
15
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
11
- import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
12
- import { createHash, randomUUID } from "node:crypto";
13
16
  import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
14
17
  import { Config as Config$1, PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
15
18
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "@earendil-works/pi-ai/api/github-copilot-headers";
19
+ //#region lib/types/dual-model-host.js
20
+ /** Optional plugin-owned planner/executor sessions; no Core/default-model mutation. */
21
+ const DUAL_MODEL_NAMESPACE = "github-copilot-dual-model";
22
+ const DUAL_MODEL_POLICY_EVENT = "github-copilot/dual-model-policy";
23
+ const DUAL_MODEL_PROJECTION = "githubCopilotDualModelPolicy";
24
+ const DUAL_MODEL_EXECUTE_TOOL = "copilot_execute";
25
+ const DEFAULT_CONFIG = Object.freeze({
26
+ enabled: false,
27
+ plannerModel: "",
28
+ executorModel: ""
29
+ });
30
+ const configSchema = z.object({
31
+ enabled: z.boolean().default(false),
32
+ plannerModel: z.string().default(""),
33
+ executorModel: z.string().default("")
34
+ });
35
+ const ConfigJson = z$1.object({
36
+ enabled: z$1.boolean(),
37
+ plannerModel: z$1.string().max(512),
38
+ executorModel: z$1.string().max(512)
39
+ }).strict();
40
+ const RevisionJson = z$1.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
41
+ const CreateJson = z$1.object({
42
+ requestId: z$1.string().uuid(),
43
+ workspaceId: z$1.string().min(1).max(512),
44
+ expectedRevision: RevisionJson
45
+ }).strict();
46
+ const SaveJson = z$1.object({
47
+ configuration: ConfigJson,
48
+ expectedRevision: RevisionJson
49
+ }).strict();
50
+ const CatalogEntryJson = z$1.object({
51
+ id: z$1.string().min(1).max(512),
52
+ name: z$1.string().min(1).max(1024)
53
+ }).strict();
54
+ const ExecuteJson = z$1.object({
55
+ description: z$1.string().min(1).max(160),
56
+ prompt: z$1.string().min(1).max(1e5)
57
+ }).strict();
58
+ const PolicyJson = z$1.object({
59
+ version: z$1.literal(1),
60
+ rootSessionId: z$1.string(),
61
+ requestId: z$1.string(),
62
+ workspaceId: z$1.string(),
63
+ settingsRevision: z$1.number().int().nonnegative(),
64
+ cwd: z$1.string(),
65
+ agentPreset: z$1.string(),
66
+ provider: z$1.literal(GITHUB_COPILOT_PREVIEW_PROVIDER_ID),
67
+ plannerModel: z$1.string().min(1),
68
+ executorModel: z$1.string().min(1),
69
+ executorTools: z$1.array(z$1.string()),
70
+ plannerTools: z$1.array(z$1.string())
71
+ }).strict();
72
+ const ChildJson = z$1.object({
73
+ version: z$1.literal(1),
74
+ mode: z$1.literal("continuable"),
75
+ provider: z$1.literal("spawn"),
76
+ agentProvider: z$1.string(),
77
+ agentModel: z$1.string()
78
+ });
79
+ const ProjectionJson = z$1.object({
80
+ id: z$1.string(),
81
+ parentId: z$1.string().nullable(),
82
+ origin: z$1.string().nullable(),
83
+ inherited: z$1.number(),
84
+ policy: PolicyJson.nullable(),
85
+ child: ChildJson.nullable(),
86
+ invalid: z$1.boolean()
87
+ });
88
+ /** A root policy is authoritative only in its own suffix, never in a fork seed. */
89
+ const dualModelProjection = {
90
+ key: DUAL_MODEL_PROJECTION,
91
+ stateVersion: 1,
92
+ stateSchema: ProjectionJson,
93
+ init: (header, inherited = 0) => ({
94
+ id: header.id,
95
+ parentId: header.parentSession ?? null,
96
+ origin: header.origin ?? null,
97
+ inherited,
98
+ policy: null,
99
+ child: null,
100
+ invalid: false
101
+ }),
102
+ apply: (state, event) => {
103
+ if (event.seq < state.inherited) return state;
104
+ if (event.type === "github-copilot/dual-model-policy") {
105
+ const parsed = PolicyJson.safeParse(event.data);
106
+ if (state.policy !== null || !parsed.success || parsed.data.rootSessionId !== state.id || state.parentId !== null || event.ignorable !== true) return {
107
+ ...state,
108
+ invalid: true
109
+ };
110
+ return {
111
+ ...state,
112
+ policy: parsed.data
113
+ };
114
+ }
115
+ if (event.type === "subagent/descriptor" && state.origin === "subagent") {
116
+ const parsed = ChildJson.safeParse(event.data);
117
+ return {
118
+ ...state,
119
+ child: parsed.success ? parsed.data : null
120
+ };
121
+ }
122
+ return state;
123
+ }
124
+ };
125
+ const ROOT_TOOLS = [
126
+ DUAL_MODEL_EXECUTE_TOOL,
127
+ "read",
128
+ "read_image",
129
+ "glob",
130
+ "grep",
131
+ "skill",
132
+ "ask_user_question",
133
+ "todo_write",
134
+ "send_message",
135
+ "list_agents",
136
+ "interrupt_agent"
137
+ ];
138
+ const EXECUTOR_TOOLS = [
139
+ "read",
140
+ "read_image",
141
+ "glob",
142
+ "grep",
143
+ "skill",
144
+ "write",
145
+ "edit",
146
+ "pwsh",
147
+ "bash",
148
+ "present",
149
+ "todo_write",
150
+ "send_message",
151
+ "list_agents",
152
+ "interrupt_agent",
153
+ "job_list",
154
+ "job_output",
155
+ "job_kill"
156
+ ];
157
+ const EXECUTOR_PERSONA = "You are the execution agent for a dedicated Copilot planner/executor session. Implement only the delegated task. Your provider and model are fixed by the session policy. Do not create other agents, workflows, dynamic plugins, or alternate model calls. Report the changed files, verification, limitations, and actual results to your direct parent with send_message. Do not claim tests or work you did not perform.";
158
+ function roleText(policy, role) {
159
+ return role === "planner" ? `You are the planning and acceptance agent. Read the current code and evidence, plan, clarify, delegate implementation through ${DUAL_MODEL_EXECUTE_TOOL}, and independently review results. Do not implement changes or run commands directly. Your planning model is ${policy.plannerModel}; execution is fixed to ${policy.executorModel}. Use the original send_message/list_agents/interrupt_agent controls to continue and inspect your execution children. Provider: ${GITHUB_COPILOT_PREVIEW_PROVIDER_ID}.` : EXECUTOR_PERSONA;
160
+ }
161
+ function fail$1(reason, creation) {
162
+ throw new RemoteError("copilot/dual-model", reason, {
163
+ reason,
164
+ ...creation === void 0 ? {} : { creation }
165
+ });
166
+ }
167
+ function creationOf(error) {
168
+ return ownRemoteFailure(error)?.creation ?? "uncertain";
169
+ }
170
+ function object$3(value) {
171
+ return typeof value === "object" && value !== null && !Array.isArray(value);
172
+ }
173
+ function api$1(ctx, key, methods) {
174
+ const value = ctx.get(key);
175
+ return object$3(value) && methods.every((method) => typeof value[method] === "function") ? value : void 0;
176
+ }
177
+ function revision(value) {
178
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
179
+ }
180
+ const ERROR_REASONS = /* @__PURE__ */ new Set([
181
+ "DUAL_MODEL_UNSUPPORTED",
182
+ "DUAL_MODEL_READ_ONLY",
183
+ "DUAL_MODEL_REVISION_CONFLICT",
184
+ "DUAL_MODEL_MODEL_UNAVAILABLE",
185
+ "DUAL_MODEL_WORKSPACE_UNAVAILABLE",
186
+ "DUAL_MODEL_DISABLED",
187
+ "DUAL_MODEL_INVALID_REQUEST",
188
+ "DUAL_MODEL_REQUEST_CONFLICT",
189
+ "DUAL_MODEL_SAVE_FAILED",
190
+ "DUAL_MODEL_CREATE_UNCERTAIN",
191
+ "DUAL_MODEL_POLICY_INVALID",
192
+ "DUAL_MODEL_SELECTION_LOCKED",
193
+ "DUAL_MODEL_DELEGATION_DENIED",
194
+ "DUAL_MODEL_EXECUTION_FAILED"
195
+ ]);
196
+ /** Remote errors cross bundle/realm boundaries; constructor identity is not a protocol. */
197
+ function ownRemoteFailure(error) {
198
+ try {
199
+ if (!object$3(error) || error.isDSHRemoteError !== true || error.code !== "copilot/dual-model" || !object$3(error.details)) return void 0;
200
+ const reason = error.details.reason;
201
+ if (typeof reason !== "string" || !ERROR_REASONS.has(reason)) return void 0;
202
+ const creation = error.details.creation;
203
+ return {
204
+ reason,
205
+ ...creation === "not-created" || creation === "uncertain" ? { creation } : {}
206
+ };
207
+ } catch {
208
+ return;
209
+ }
210
+ }
211
+ function reasonOf(error, fallback) {
212
+ const own = ownRemoteFailure(error);
213
+ if (own) return own.reason;
214
+ try {
215
+ if (object$3(error) && error.code === "SETTINGS_CONFLICT") return "DUAL_MODEL_REVISION_CONFLICT";
216
+ } catch {}
217
+ return fallback;
218
+ }
219
+ function dualModelSessionId(requestId) {
220
+ return `copilot-dual-${createHash("sha256").update(requestId).digest("hex")}`;
221
+ }
222
+ function requestKey(request) {
223
+ return JSON.stringify([
224
+ request.requestId,
225
+ request.workspaceId,
226
+ request.expectedRevision
227
+ ]);
228
+ }
229
+ function policyMatches(policy, request) {
230
+ return policy.requestId === request.requestId && policy.workspaceId === request.workspaceId && policy.settingsRevision === request.expectedRevision;
231
+ }
232
+ function dedicatedAddress(agent) {
233
+ const owned = /^copilot-dual-[0-9a-f]{64}$/;
234
+ return owned.test(agent.id) || agent.session.header.origin === "subagent" && owned.test(agent.session.header.parentSession ?? "");
235
+ }
236
+ /** Always mountable; missing optional APIs produce a safe unsupported view. */
237
+ var GitHubCopilotDualModel = class extends Service {
238
+ owner;
239
+ lifetime = new AbortController();
240
+ creates = /* @__PURE__ */ new Map();
241
+ overlays = /* @__PURE__ */ new Map();
242
+ handles = /* @__PURE__ */ new Map();
243
+ settingsReady = false;
244
+ projectionReady = false;
245
+ registrationFailed = false;
246
+ constructor(ctx) {
247
+ super(ctx, "githubCopilotDualModel");
248
+ this.owner = { ctx };
249
+ ctx.inject(["settings"], (scope) => {
250
+ const settings = api$1(scope, "settings", [
251
+ "register",
252
+ "describe",
253
+ "replace"
254
+ ]);
255
+ if (!settings) return;
256
+ try {
257
+ settings.register(DUAL_MODEL_NAMESPACE, configSchema);
258
+ this.settingsReady = true;
259
+ } catch {
260
+ this.registrationFailed = true;
261
+ return;
262
+ }
263
+ scope.effect(() => () => {
264
+ this.settingsReady = false;
265
+ });
266
+ });
267
+ ctx.inject(["sessionProjections"], (scope) => {
268
+ const projections = api$1(scope, "sessionProjections", ["register", "stateOf"]);
269
+ if (!projections) return;
270
+ try {
271
+ projections.register(dualModelProjection);
272
+ this.projectionReady = true;
273
+ } catch {
274
+ this.registrationFailed = true;
275
+ return;
276
+ }
277
+ scope.effect(() => () => {
278
+ this.projectionReady = false;
279
+ });
280
+ });
281
+ ctx.inject(["tools"], (scope) => {
282
+ api$1(scope, "tools", ["guard"])?.guard((exec) => exec.agent && dedicatedAddress(exec.agent) && !this.overlays.has(exec.agent) ? "DUAL_MODEL_UNSUPPORTED" : void 0);
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;
303
+ return {
304
+ ...result,
305
+ sections: result.sections.some((section) => section.name === sectionName) ? result.sections : [...result.sections, {
306
+ name: sectionName,
307
+ text: roleText(policy, role)
308
+ }],
309
+ tools: schemas.filter((tool) => allowed.has(tool.name)),
310
+ variables: {
311
+ ...result.variables,
312
+ provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
313
+ model: role === "planner" ? policy.plannerModel : policy.executorModel
314
+ }
315
+ };
316
+ }, { prepend: true });
317
+ ctx.on("agent/created", ({ agent }) => this.restore(agent));
318
+ ctx.on("agent/disposed", ({ agent }) => {
319
+ const subject = agent;
320
+ const overlay = this.overlays.get(subject);
321
+ this.overlays.delete(subject);
322
+ if (this.handles.get(agent.id)?.agent === subject) this.handles.delete(agent.id);
323
+ if (overlay) overlay.dispose().catch(() => {});
324
+ });
325
+ ctx.effect(() => async () => {
326
+ this.lifetime.abort();
327
+ await Promise.allSettled([...this.creates.values()].map((value) => value.promise));
328
+ const roots = [...this.overlays].filter(([, value]) => value.role === "planner").map(([agent]) => agent);
329
+ await api$1(ctx, "subagents", ["drainContinuableDescendants"])?.drainContinuableDescendants(roots).catch(() => {});
330
+ for (const [agent] of this.overlays) agent.cancel({ kind: "disposed" });
331
+ await Promise.allSettled([...this.overlays.keys()].map((agent) => agent.whenIdle()));
332
+ await Promise.allSettled([...this.handles.values()].map((handle) => handle.dispose()));
333
+ await Promise.allSettled([...this.overlays.values()].map((overlay) => overlay.dispose()));
334
+ this.handles.clear();
335
+ this.overlays.clear();
336
+ });
337
+ for (const agent of api$1(ctx, "agents", ["list"])?.list() ?? []) try {
338
+ this.restore(agent);
339
+ } catch {}
340
+ }
341
+ capabilities() {
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
430
+ };
431
+ let models = [], diagnostic;
432
+ try {
433
+ const preview = await cap.preview.discover({
434
+ force: false,
435
+ signal: this.lifetime.signal
436
+ });
437
+ if (preview.available && preview.state === "ready") models = z$1.array(CatalogEntryJson).max(512).parse(preview.models.map(({ id, name }) => ({
438
+ id,
439
+ name
440
+ })));
441
+ else diagnostic = "DUAL_MODEL_MODEL_UNAVAILABLE";
442
+ } catch {
443
+ diagnostic = "DUAL_MODEL_MODEL_UNAVAILABLE";
444
+ }
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
+ }
459
+ }
460
+ async save(input) {
461
+ try {
462
+ return await this.saveOnce(input);
463
+ } catch (error) {
464
+ fail$1(reasonOf(error, "DUAL_MODEL_SAVE_FAILED"));
465
+ }
466
+ }
467
+ async saveOnce(input) {
468
+ const parsed = SaveJson.safeParse(input);
469
+ if (!parsed.success) fail$1("DUAL_MODEL_INVALID_REQUEST");
470
+ const cap = this.requireCapabilities(), { configuration, expectedRevision } = parsed.data;
471
+ if (cap.settings.writable !== true) fail$1("DUAL_MODEL_READ_ONLY");
472
+ if (this.configuration().revision !== expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
473
+ if (configuration.enabled) await this.assertModels(cap, [configuration.plannerModel, configuration.executorModel]);
474
+ try {
475
+ await cap.settings.replace(DUAL_MODEL_NAMESPACE, configuration, expectedRevision);
476
+ } catch (error) {
477
+ fail$1(reasonOf(error, "DUAL_MODEL_SAVE_FAILED"));
478
+ }
479
+ return this.view();
480
+ }
481
+ create(input) {
482
+ const parsed = CreateJson.safeParse(input);
483
+ if (!parsed.success) return Promise.reject(new RemoteError("copilot/dual-model", "DUAL_MODEL_INVALID_REQUEST", { reason: "DUAL_MODEL_INVALID_REQUEST" }));
484
+ const request = parsed.data, id = dualModelSessionId(request.requestId), key = requestKey(request);
485
+ const pending = this.creates.get(id);
486
+ if (pending) return pending.key === key ? pending.promise : Promise.reject(new RemoteError("copilot/dual-model", "DUAL_MODEL_REQUEST_CONFLICT", { reason: "DUAL_MODEL_REQUEST_CONFLICT" }));
487
+ const promise = this.createOnce(request, id).catch((error) => fail$1(reasonOf(error, "DUAL_MODEL_CREATE_UNCERTAIN"), creationOf(error)));
488
+ this.creates.set(id, {
489
+ key,
490
+ promise
491
+ });
492
+ promise.then(() => this.creates.delete(id), () => this.creates.delete(id));
493
+ return promise;
494
+ }
495
+ async createOnce(request, id) {
496
+ let creation = "uncertain";
497
+ try {
498
+ const cap = this.requireCapabilities();
499
+ if (await cap.persistence.stat(id) !== void 0 || cap.agents.get(id)) {
500
+ const inspection = await cap.inspector.inspect(id);
501
+ let state = dualModelProjection.init(inspection.meta, inspection.inheritedEventCount);
502
+ for (const event of inspection.events) state = dualModelProjection.apply(state, event);
503
+ if (state.invalid || !state.policy || !policyMatches(state.policy, request)) fail$1("DUAL_MODEL_REQUEST_CONFLICT");
504
+ const workspace = this.workspace(cap, request.workspaceId);
505
+ if (workspace.path !== state.policy.cwd) fail$1("DUAL_MODEL_REQUEST_CONFLICT");
506
+ await workspace.attachSession(id);
507
+ return { sessionId: id };
508
+ }
509
+ creation = "not-created";
510
+ const current = this.configuration();
511
+ if (current.revision !== request.expectedRevision) fail$1("DUAL_MODEL_REVISION_CONFLICT");
512
+ if (!current.configuration.enabled) fail$1("DUAL_MODEL_DISABLED");
513
+ const workspace = this.workspace(cap, request.workspaceId);
514
+ const captured = { ...current.configuration };
515
+ await this.assertModels(cap, [captured.plannerModel, captured.executorModel]);
516
+ const preset = await cap.presets.resolve();
517
+ const standingKey = await cap.presets.standingKeyFor(preset.id);
518
+ const inheritedNames = new Set(cap.tools.schemas(standingKey).map((tool) => tool.name));
519
+ const executorTools = EXECUTOR_TOOLS.filter((name) => inheritedNames.has(name));
520
+ if (!executorTools.includes("send_message")) fail$1("DUAL_MODEL_UNSUPPORTED");
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,
546
+ agentPreset: preset.id,
547
+ isSeeded: false
548
+ },
549
+ inheritedEventCount: 0,
550
+ seed: [{
551
+ type: DUAL_MODEL_POLICY_EVENT,
552
+ seq: 0,
553
+ time: Date.now(),
554
+ ignorable: true,
555
+ data: policy
556
+ }, {
557
+ type: "model/selection",
558
+ seq: 1,
559
+ time: Date.now(),
560
+ data: {
561
+ provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
562
+ model: policy.plannerModel
563
+ }
564
+ }],
565
+ signal: this.lifetime.signal,
566
+ setup: async (agentCtx, agent) => {
567
+ await cap.presets.mount(agentCtx, preset.id);
568
+ this.install(agent, policy, "planner");
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");
599
+ }
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
+ }
619
+ if (state.invalid) fail$1("DUAL_MODEL_POLICY_INVALID");
620
+ if (state.policy) {
621
+ this.install(agent, state.policy, "planner");
622
+ return;
623
+ }
624
+ if (state.origin !== "subagent" || state.parentId === null) {
625
+ if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_POLICY_INVALID");
626
+ return;
627
+ }
628
+ const parent = api$1(this.owner.ctx, "agents", ["get"])?.get(state.parentId);
629
+ if (!parent) {
630
+ if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_UNSUPPORTED");
631
+ return;
632
+ }
633
+ if (this.overlays.get(parent)?.role === "executor") fail$1("DUAL_MODEL_DELEGATION_DENIED");
634
+ const parentPolicy = this.projection(parent)?.policy;
635
+ if (!parentPolicy) {
636
+ if (dedicatedAddress(agent)) fail$1("DUAL_MODEL_POLICY_INVALID");
637
+ return;
638
+ }
639
+ if (!state.child || state.child.agentProvider !== "github-copilot-preview" || state.child.agentModel !== parentPolicy.executorModel) fail$1("DUAL_MODEL_POLICY_INVALID");
640
+ this.install(agent, parentPolicy, "executor");
641
+ }
642
+ install(agent, policy, role) {
643
+ if (this.overlays.has(agent)) return;
644
+ this.requireCapabilities();
645
+ if (typeof agent.cancel !== "function" || typeof agent.whenIdle !== "function") fail$1("DUAL_MODEL_UNSUPPORTED");
646
+ const overlay = dshScope.createScope(agent.ctx, agent), scoped = overlay.ctx;
647
+ try {
648
+ const tools = api$1(scoped, "tools", [
649
+ "schemas",
650
+ "register",
651
+ "restrict",
652
+ "guard",
653
+ "presentAs"
654
+ ]);
655
+ const prompt = api$1(scoped, "systemPrompt", ["section"]);
656
+ if (!tools || !prompt) fail$1("DUAL_MODEL_UNSUPPORTED");
657
+ const allowed = new Set(role === "planner" ? policy.plannerTools : policy.executorTools);
658
+ const available = tools.schemas(agent).map((item) => item.name);
659
+ tools.guard((exec) => allowed.has(exec.name) ? void 0 : "DUAL_MODEL_TOOL_DENIED");
660
+ tools.presentAs("native");
661
+ tools.restrict({ allow: available.filter((name) => allowed.has(name) && name !== "copilot_execute") });
662
+ const selectedModel = role === "planner" ? policy.plannerModel : policy.executorModel;
663
+ prompt.section({
664
+ name: "github-copilot:dual-model-role",
665
+ order: 1e3,
666
+ text: roleText(policy, role)
667
+ });
668
+ scoped.on("agent/request", async ({ signal }, next) => {
669
+ await this.assertModels(this.requireCapabilities(), [selectedModel], signal);
670
+ const resolved = await next();
671
+ if (resolved.provider !== "github-copilot-preview" || resolved.model !== selectedModel) fail$1("DUAL_MODEL_SELECTION_LOCKED");
672
+ return resolved;
673
+ }, { prepend: true });
674
+ if (role === "planner") tools.register({
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: {
688
+ type: "object",
689
+ additionalProperties: false,
690
+ properties: {
691
+ childId: { type: "string" },
692
+ provider: { type: "string" },
693
+ model: { type: "string" }
694
+ },
695
+ required: [
696
+ "childId",
697
+ "provider",
698
+ "model"
699
+ ]
700
+ },
701
+ render: (_args, value) => [{
702
+ type: "text",
703
+ text: JSON.stringify(value)
704
+ }]
705
+ },
706
+ execute: async (input, exec) => {
707
+ try {
708
+ const parsed = ExecuteJson.safeParse(input);
709
+ if (!parsed.success || exec.agent !== agent) fail$1("DUAL_MODEL_INVALID_REQUEST");
710
+ const current = this.requireCapabilities();
711
+ await this.assertModels(current, [policy.executorModel], exec.signal);
712
+ return {
713
+ childId: (await current.subagents.startContinuable({
714
+ provider: "spawn",
715
+ label: parsed.data.description,
716
+ request: {
717
+ parent: agent,
718
+ prompt: [{
719
+ type: "text",
720
+ text: parsed.data.prompt
721
+ }],
722
+ agentOptions: {
723
+ provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
724
+ model: policy.executorModel
725
+ },
726
+ persona: EXECUTOR_PERSONA,
727
+ toolFilter: { allow: policy.executorTools },
728
+ maxDepth: 1
729
+ },
730
+ signal: exec.signal
731
+ })).childId,
732
+ provider: GITHUB_COPILOT_PREVIEW_PROVIDER_ID,
733
+ model: policy.executorModel
734
+ };
735
+ } catch (error) {
736
+ fail$1(reasonOf(error, "DUAL_MODEL_EXECUTION_FAILED"));
737
+ }
738
+ }
739
+ });
740
+ this.overlays.set(agent, {
741
+ dispose: overlay.dispose,
742
+ policy,
743
+ role
744
+ });
745
+ } catch (error) {
746
+ overlay.dispose().catch(() => {});
747
+ throw error;
748
+ }
749
+ }
750
+ };
751
+ //#endregion
16
752
  //#region package.json
17
753
  var name$1 = "dsh-github-copilot";
18
- var version = "0.4.0-alpha.18";
754
+ var version = "0.4.0-alpha.20";
19
755
  //#endregion
20
756
  //#region lib/types/probe.js
21
757
  /**
@@ -1929,9 +2665,9 @@ async function createDeepSeekSearchFallback(ctx, owner, canContinue) {
1929
2665
  * @module dsh-github-copilot/compatibility
1930
2666
  */
1931
2667
  const DSH_COMPATIBILITY = {
1932
- release: "0.1.5-alpha.2",
2668
+ release: "0.1.6-alpha.1",
1933
2669
  developmentRelease: "0.1.2-rc.1",
1934
- peerRange: "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2",
2670
+ peerRange: "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1",
1935
2671
  supportedReleases: [
1936
2672
  "0.1.1-rc.2",
1937
2673
  "0.1.2-rc.1",
@@ -1939,7 +2675,8 @@ const DSH_COMPATIBILITY = {
1939
2675
  "0.1.5-alpha.1",
1940
2676
  "0.1.5-alpha.2",
1941
2677
  "0.1.5-rc.1",
1942
- "0.1.5-rc.2"
2678
+ "0.1.5-rc.2",
2679
+ "0.1.6-alpha.1"
1943
2680
  ],
1944
2681
  requiredApis: [
1945
2682
  "agentDefaultModel.currentSelection",
@@ -4990,6 +5727,7 @@ function activate(ctx, config) {
4990
5727
  });
4991
5728
  ctx.plugin(preview_route_default, { accountModelSettings: () => current() });
4992
5729
  ctx.plugin(GitHubCopilotAuthorizationController);
5730
+ ctx.plugin(GitHubCopilotDualModel);
4993
5731
  const resolveGitHubCopilotToken = createGitHubCopilotTokenResolver(ctx, async () => {
4994
5732
  await ensureGitHubCopilotProviderProfile(ctx);
4995
5733
  });