dsh-github-copilot 0.4.0-alpha.19 → 0.4.0-alpha.21

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/client.js CHANGED
@@ -5366,6 +5366,102 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5366
5366
  return /* @__PURE__ */ _superRefine(fn, params);
5367
5367
  }
5368
5368
  //#endregion
5369
+ //#region lib/types/dual-model-remote.js
5370
+ const id = string().min(1).max(512);
5371
+ const revision = number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
5372
+ const DualModelConfigSchema = object({
5373
+ enabled: boolean(),
5374
+ plannerModel: string().max(512),
5375
+ executorModel: string().max(512)
5376
+ }).strict();
5377
+ const DualModelViewSchema = object({
5378
+ supported: boolean(),
5379
+ diagnostic: string().min(1).max(128).optional(),
5380
+ writable: boolean(),
5381
+ revision: revision.nullable(),
5382
+ configuration: DualModelConfigSchema,
5383
+ models: array(object({
5384
+ id,
5385
+ name: string().min(1).max(1024)
5386
+ }).strict()).max(512),
5387
+ workspaces: array(object({
5388
+ id,
5389
+ name: string().min(1).max(1024)
5390
+ }).strict()).max(1024)
5391
+ }).strict();
5392
+ const DualModelSaveSchema = object({
5393
+ configuration: DualModelConfigSchema,
5394
+ expectedRevision: revision
5395
+ }).strict();
5396
+ const DualModelCreateSchema = object({
5397
+ requestId: string().uuid(),
5398
+ workspaceId: id,
5399
+ expectedRevision: revision
5400
+ }).strict();
5401
+ const DualModelCreateResultSchema = object({ sessionId: id }).strict();
5402
+ const contribution$1 = {
5403
+ package: "dsh-github-copilot",
5404
+ descriptors: [
5405
+ {
5406
+ id: "dsh-github-copilot:githubCopilotDualModel.view",
5407
+ namespace: "githubCopilotDualModel",
5408
+ service: "githubCopilotDualModel",
5409
+ method: "view",
5410
+ invocation: { kind: "direct" },
5411
+ parameters: [],
5412
+ result: {
5413
+ mode: "strict",
5414
+ typeSymbol: "dsh-github-copilot#DualModelView",
5415
+ schema: DualModelViewSchema
5416
+ }
5417
+ },
5418
+ {
5419
+ id: "dsh-github-copilot:githubCopilotDualModel.save",
5420
+ namespace: "githubCopilotDualModel",
5421
+ service: "githubCopilotDualModel",
5422
+ method: "save",
5423
+ invocation: { kind: "direct" },
5424
+ parameters: [{
5425
+ name: "input",
5426
+ wire: "input",
5427
+ source: "json",
5428
+ codec: {
5429
+ mode: "strict",
5430
+ typeSymbol: "dsh-github-copilot#DualModelSaveRequest",
5431
+ schema: DualModelSaveSchema
5432
+ }
5433
+ }],
5434
+ result: {
5435
+ mode: "strict",
5436
+ typeSymbol: "dsh-github-copilot#DualModelView",
5437
+ schema: DualModelViewSchema
5438
+ }
5439
+ },
5440
+ {
5441
+ id: "dsh-github-copilot:githubCopilotDualModel.create",
5442
+ namespace: "githubCopilotDualModel",
5443
+ service: "githubCopilotDualModel",
5444
+ method: "create",
5445
+ invocation: { kind: "direct" },
5446
+ parameters: [{
5447
+ name: "input",
5448
+ wire: "input",
5449
+ source: "json",
5450
+ codec: {
5451
+ mode: "strict",
5452
+ typeSymbol: "dsh-github-copilot#DualModelCreateRequest",
5453
+ schema: DualModelCreateSchema
5454
+ }
5455
+ }],
5456
+ result: {
5457
+ mode: "strict",
5458
+ typeSymbol: "dsh-github-copilot#DualModelCreateResult",
5459
+ schema: DualModelCreateResultSchema
5460
+ }
5461
+ }
5462
+ ]
5463
+ };
5464
+ //#endregion
5369
5465
  //#region lib/types/remote.js
5370
5466
  /**
5371
5467
  * Client-safe Remote contribution for the plugin-owned authorization bridge.
@@ -5495,35 +5591,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5495
5591
  }).strict();
5496
5592
  const contribution = {
5497
5593
  package: "dsh-github-copilot",
5498
- descriptors: [...[
5499
- "status",
5500
- "reconcile",
5501
- "discoverModels",
5502
- "ensureModels",
5503
- "start",
5504
- "cancel",
5505
- "signOut"
5506
- ].map((method) => ({
5507
- id: `dsh-github-copilot:githubCopilot.${method}`,
5508
- service: "githubCopilotAuthorization",
5509
- namespace: "githubCopilot",
5510
- method,
5511
- invocation: direct,
5512
- parameters: [],
5513
- result
5514
- })), {
5515
- id: "dsh-github-copilot:githubCopilot.migrationStatus",
5516
- service: "githubCopilotAuthorization",
5517
- namespace: "githubCopilot",
5518
- method: "migrationStatus",
5519
- invocation: direct,
5520
- parameters: [],
5521
- result: {
5522
- mode: "strict",
5523
- typeSymbol: GITHUB_COPILOT_MIGRATION_STATUS_TYPE_SYMBOL,
5524
- schema: GitHubCopilotMigrationStatusSchema
5525
- }
5526
- }]
5594
+ descriptors: [
5595
+ ...[
5596
+ "status",
5597
+ "reconcile",
5598
+ "discoverModels",
5599
+ "ensureModels",
5600
+ "start",
5601
+ "cancel",
5602
+ "signOut"
5603
+ ].map((method) => ({
5604
+ id: `dsh-github-copilot:githubCopilot.${method}`,
5605
+ service: "githubCopilotAuthorization",
5606
+ namespace: "githubCopilot",
5607
+ method,
5608
+ invocation: direct,
5609
+ parameters: [],
5610
+ result
5611
+ })),
5612
+ {
5613
+ id: "dsh-github-copilot:githubCopilot.migrationStatus",
5614
+ service: "githubCopilotAuthorization",
5615
+ namespace: "githubCopilot",
5616
+ method: "migrationStatus",
5617
+ invocation: direct,
5618
+ parameters: [],
5619
+ result: {
5620
+ mode: "strict",
5621
+ typeSymbol: GITHUB_COPILOT_MIGRATION_STATUS_TYPE_SYMBOL,
5622
+ schema: GitHubCopilotMigrationStatusSchema
5623
+ }
5624
+ },
5625
+ ...contribution$1.descriptors
5626
+ ]
5527
5627
  };
5528
5628
  //#endregion
5529
5629
  //#region lib/types/copilot-identity.js
@@ -5788,7 +5888,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5788
5888
  const DEEPSEEK_SEARCH_PROVIDER = "deepseek-official";
5789
5889
  const EXA_SEARCH_PROVIDER = "exa";
5790
5890
  const PERPLEXITY_SEARCH_PROVIDER = "perplexity";
5791
- const cardStyle = {
5891
+ const cardStyle$1 = {
5792
5892
  display: "grid",
5793
5893
  gap: "14px",
5794
5894
  padding: "18px",
@@ -5797,11 +5897,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5797
5897
  borderRadius: "14px",
5798
5898
  background: "color-mix(in srgb, currentColor 4%, transparent)"
5799
5899
  };
5800
- const fieldStyle = {
5900
+ const fieldStyle$1 = {
5801
5901
  display: "grid",
5802
5902
  gap: "6px"
5803
5903
  };
5804
- const controlStyle = {
5904
+ const controlStyle$1 = {
5805
5905
  width: "100%",
5806
5906
  padding: "9px 11px",
5807
5907
  borderRadius: "9px",
@@ -5810,7 +5910,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5810
5910
  background: "color-mix(in srgb, currentColor 3%, transparent)",
5811
5911
  font: "inherit"
5812
5912
  };
5813
- const buttonStyle = {
5913
+ const buttonStyle$1 = {
5814
5914
  justifySelf: "start",
5815
5915
  padding: "9px 16px",
5816
5916
  borderRadius: "999px",
@@ -6003,7 +6103,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6003
6103
  usesCopilot
6004
6104
  ]);
6005
6105
  return (0, react.createElement)("section", {
6006
- style: cardStyle,
6106
+ style: cardStyle$1,
6007
6107
  "data-dsh-web-search-routing": true
6008
6108
  }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { style: {
6009
6109
  margin: 0,
@@ -6013,11 +6113,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6013
6113
  opacity: .72,
6014
6114
  fontSize: "13px",
6015
6115
  lineHeight: 1.5
6016
- } }, "Auto tries native search for the initiating Copilot model, then the default provider when unavailable. Other model providers use the default directly. Fixed always uses the default. Chat remains available when search is unavailable.")), (0, react.createElement)("label", { style: fieldStyle }, (0, react.createElement)("span", { style: {
6116
+ } }, "Auto tries native search for the initiating Copilot model, then the default provider when unavailable. Other model providers use the default directly. Fixed always uses the default. Chat remains available when search is unavailable.")), (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
6017
6117
  fontSize: "13px",
6018
6118
  fontWeight: 600
6019
6119
  } }, "Search mode"), (0, react.createElement)("select", {
6020
- style: controlStyle,
6120
+ style: controlStyle$1,
6021
6121
  disabled,
6022
6122
  value: draft.mode,
6023
6123
  "data-dsh-web-search-mode": true,
@@ -6028,11 +6128,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6028
6128
  mode
6029
6129
  }));
6030
6130
  }
6031
- }, (0, react.createElement)("option", { value: "auto" }, "Auto — native Copilot search first, then default provider"), (0, react.createElement)("option", { value: "fixed" }, "Fixed — always use the default provider"))), (0, react.createElement)("label", { style: fieldStyle }, (0, react.createElement)("span", { style: {
6131
+ }, (0, react.createElement)("option", { value: "auto" }, "Auto — native Copilot search first, then default provider"), (0, react.createElement)("option", { value: "fixed" }, "Fixed — always use the default provider"))), (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
6032
6132
  fontSize: "13px",
6033
6133
  fontWeight: 600
6034
6134
  } }, "Default search provider"), (0, react.createElement)("input", {
6035
- style: controlStyle,
6135
+ style: controlStyle$1,
6036
6136
  list: "dsh-search-provider-options",
6037
6137
  disabled,
6038
6138
  value: draft.provider,
@@ -6052,11 +6152,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6052
6152
  }))), (0, react.createElement)("span", { style: {
6053
6153
  opacity: .66,
6054
6154
  fontSize: "12px"
6055
- } }, "Suggestions are examples, not installed-provider or capability checks. Enter any registered search provider id, or none; unavailable providers fail without affecting chat.")), !usesCopilot ? null : (0, react.createElement)("label", { style: fieldStyle }, (0, react.createElement)("span", { style: {
6155
+ } }, "Suggestions are examples, not installed-provider or capability checks. Enter any registered search provider id, or none; unavailable providers fail without affecting chat.")), !usesCopilot ? null : (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
6056
6156
  fontSize: "13px",
6057
6157
  fontWeight: 600
6058
6158
  } }, "Copilot search model"), (0, react.createElement)("input", {
6059
- style: controlStyle,
6159
+ style: controlStyle$1,
6060
6160
  list: "dsh-copilot-search-models",
6061
6161
  disabled: disabled || missingCopilot,
6062
6162
  value: draft.model,
@@ -6085,7 +6185,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6085
6185
  flexWrap: "wrap"
6086
6186
  } }, (0, react.createElement)("button", {
6087
6187
  type: "button",
6088
- style: buttonStyle,
6188
+ style: buttonStyle$1,
6089
6189
  disabled: disabled || missingCopilot,
6090
6190
  "data-dsh-web-search-save": true,
6091
6191
  onClick: () => {
@@ -6093,7 +6193,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6093
6193
  }
6094
6194
  }, loading ? "Loading…" : saving ? "Saving…" : "Save search routing"), (0, react.createElement)("button", {
6095
6195
  type: "button",
6096
- style: buttonStyle,
6196
+ style: buttonStyle$1,
6097
6197
  disabled: loading || saving,
6098
6198
  title: "Reload saved values and discard unsaved edits",
6099
6199
  "data-dsh-web-search-reload": true,
@@ -6109,6 +6209,582 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6109
6209
  }, message)));
6110
6210
  }
6111
6211
  //#endregion
6212
+ //#region lib/types/dual-model-card.js
6213
+ const creations = /* @__PURE__ */ new WeakMap();
6214
+ const off = {
6215
+ enabled: false,
6216
+ plannerModel: "",
6217
+ executorModel: ""
6218
+ };
6219
+ const copy = {
6220
+ en: {
6221
+ title: "Model roles",
6222
+ description: "The planning model plans and reviews acceptance. The execution model changes code and runs tests.",
6223
+ scope: "Only new dual-model sessions use this configuration. Existing sessions and the global default stay unchanged. Models are never substituted automatically.",
6224
+ enabled: "Enable dual-model sessions",
6225
+ planner: "Planning model",
6226
+ executor: "Execution model",
6227
+ acceptance: "Acceptance always uses the planning model.",
6228
+ selectModel: "Select an available model",
6229
+ unavailable: "unavailable",
6230
+ modelsMissing: "Choose two available account models to save an enabled configuration. An unavailable saved model is kept, not replaced.",
6231
+ workspace: "Workspace for the new session",
6232
+ selectWorkspace: "Select a workspace",
6233
+ noWorkspaces: "No accessible workspaces are available. Reload settings after opening a workspace.",
6234
+ save: "Save configuration",
6235
+ saving: "Saving…",
6236
+ create: "Create session with this configuration",
6237
+ creating: "Creating session…",
6238
+ retryCreate: "Retry the same creation request",
6239
+ openCreated: "Open created session",
6240
+ reload: "Reload settings",
6241
+ reloadTitle: "Reload saved values and discard unsaved edits",
6242
+ loading: "Loading model roles…",
6243
+ dirty: "Save your changes before creating a session.",
6244
+ disabled: "Dual-model sessions are off. You can save this configuration, but cannot create a dual-model session.",
6245
+ readonly: "These settings are read-only. Ask the deployment owner to enable writes, then reload settings.",
6246
+ unsupported: "Dual-model sessions are unavailable in this deployment. Check plugin support, then reload settings.",
6247
+ revision: "A settings revision is unavailable. Reload settings before saving or creating a session.",
6248
+ conflict: "These settings changed elsewhere. Reload settings to review the saved configuration before trying again.",
6249
+ modelUnavailable: "A selected model is unavailable. Reload settings and choose available account models. No model was substituted.",
6250
+ workspaceUnavailable: "The selected workspace is unavailable. Reload settings and choose an accessible workspace.",
6251
+ loadError: "Could not load model roles. Reload settings to retry.",
6252
+ saveError: "The save was not confirmed. Reload settings to check the saved configuration before trying again.",
6253
+ uncertain: "Session creation is not confirmed. Retry the same request to recover its result without creating a duplicate. Workspace and configuration are held until confirmation.",
6254
+ pending: "A previous creation request needs confirmation. Retry the same request; it is not replayed automatically.",
6255
+ saved: "Saved. This configuration applies only to new dual-model sessions.",
6256
+ created: "Session created.",
6257
+ openError: "Session created, but navigation failed. Open the created session from the session list.",
6258
+ requestUnavailable: "Secure request IDs are unavailable in this browser. Use a secure browser context before creating a session."
6259
+ },
6260
+ zh: {
6261
+ title: "模型分工",
6262
+ description: "主模型负责规划与验收;执行模型负责修改代码和运行测试。",
6263
+ scope: "仅对新建的双模型会话生效。已有会话和全局默认模型保持不变。不会自动替换模型。",
6264
+ enabled: "启用双模型会话",
6265
+ planner: "主模型(规划)",
6266
+ executor: "执行模型",
6267
+ acceptance: "验收固定跟随主模型。",
6268
+ selectModel: "选择可用模型",
6269
+ unavailable: "不可用",
6270
+ modelsMissing: "启用时须选择两个当前账号可用的模型才能保存。已保存但不可用的模型会保留,不会自动替换。",
6271
+ workspace: "新会话的工作区",
6272
+ selectWorkspace: "选择工作区",
6273
+ noWorkspaces: "暂无可访问的工作区。打开工作区后重新加载设置。",
6274
+ save: "保存配置",
6275
+ saving: "正在保存…",
6276
+ create: "用此配置新建会话",
6277
+ creating: "正在创建会话…",
6278
+ retryCreate: "重试同一创建请求",
6279
+ openCreated: "打开已创建的会话",
6280
+ reload: "重新加载设置",
6281
+ reloadTitle: "重新读取已保存的配置,并放弃未保存的修改",
6282
+ loading: "正在加载模型分工…",
6283
+ dirty: "请先保存修改,再新建会话。",
6284
+ disabled: "双模型会话已关闭。可以保存配置,但不能新建双模型会话。",
6285
+ readonly: "当前设置为只读。请联系部署管理员启用写入后,重新加载设置。",
6286
+ unsupported: "当前部署不支持双模型会话。请检查插件支持情况后,重新加载设置。",
6287
+ revision: "无法获取设置版本。请重新加载设置后,再保存或新建会话。",
6288
+ conflict: "设置已在其他位置修改。请重新加载,检查已保存的配置后再重试。",
6289
+ modelUnavailable: "所选模型当前不可用。请重新加载设置并选择可用模型。未自动替换任何模型。",
6290
+ workspaceUnavailable: "所选工作区当前不可用。请重新加载设置并选择可访问的工作区。",
6291
+ loadError: "无法加载模型分工。请重新加载设置以重试。",
6292
+ saveError: "尚未确认保存结果。请重新加载并检查已保存的配置后再重试。",
6293
+ uncertain: "尚未确认会话创建结果。请重试同一请求以获取结果,避免重复创建。确认前将保持原工作区和配置不变。",
6294
+ pending: "上次创建请求的结果尚待确认。请重试同一请求;不会自动重新提交。",
6295
+ saved: "已保存。配置仅用于新建的双模型会话。",
6296
+ created: "会话已创建。",
6297
+ openError: "会话已创建,但无法自动打开。请从会话列表打开。",
6298
+ requestUnavailable: "当前浏览器无法生成安全的请求标识。请使用安全浏览器环境后再创建会话。"
6299
+ }
6300
+ };
6301
+ const diagnosticMessages = {
6302
+ DUAL_MODEL_UNSUPPORTED: "unsupported",
6303
+ DUAL_MODEL_READ_ONLY: "readonly",
6304
+ DUAL_MODEL_REVISION_CONFLICT: "conflict",
6305
+ DUAL_MODEL_MODEL_UNAVAILABLE: "modelUnavailable",
6306
+ DUAL_MODEL_WORKSPACE_UNAVAILABLE: "workspaceUnavailable",
6307
+ DUAL_MODEL_DISABLED: "disabled"
6308
+ };
6309
+ function diagnostic(value) {
6310
+ let code = value;
6311
+ if (typeof value === "object" && value !== null) {
6312
+ const details = "details" in value ? value.details : void 0;
6313
+ code = typeof details === "object" && details !== null && "reason" in details ? details.reason : "code" in value ? value.code : void 0;
6314
+ }
6315
+ return typeof code === "string" && Object.hasOwn(diagnosticMessages, code) ? diagnosticMessages[code] : void 0;
6316
+ }
6317
+ /** A reason alone says nothing about an earlier attempt with the same UUID. */
6318
+ function notCreatedDiagnostic(value) {
6319
+ if (typeof value !== "object" || value === null || !("details" in value)) return void 0;
6320
+ const details = value.details;
6321
+ if (typeof details !== "object" || details === null || !("creation" in details) || details.creation !== "not-created" || !("reason" in details) || typeof details.reason !== "string") return void 0;
6322
+ return diagnostic(details.reason);
6323
+ }
6324
+ function validRevision(value) {
6325
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
6326
+ }
6327
+ function validView(value) {
6328
+ const namedItems = (items) => Array.isArray(items) && items.every((item) => item && typeof item.id === "string" && item.id.length > 0 && typeof item.name === "string") && new Set(items.map((item) => item.id)).size === items.length;
6329
+ return !!value && typeof value.supported === "boolean" && typeof value.writable === "boolean" && (value.revision === null || validRevision(value.revision)) && !!value.configuration && typeof value.configuration.enabled === "boolean" && typeof value.configuration.plannerModel === "string" && typeof value.configuration.executorModel === "string" && namedItems(value.models) && namedItems(value.workspaces);
6330
+ }
6331
+ function sameConfig(a, b) {
6332
+ return a.enabled === b.enabled && a.plannerModel === b.plannerModel && a.executorModel === b.executorModel;
6333
+ }
6334
+ const cardStyle = {
6335
+ display: "grid",
6336
+ gap: "14px",
6337
+ padding: "18px",
6338
+ marginTop: "16px",
6339
+ minWidth: 0,
6340
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
6341
+ borderRadius: "14px",
6342
+ background: "color-mix(in srgb, currentColor 4%, transparent)",
6343
+ overflowWrap: "anywhere"
6344
+ };
6345
+ const fieldStyle = {
6346
+ display: "grid",
6347
+ gap: "6px",
6348
+ minWidth: 0
6349
+ };
6350
+ const textStyle = {
6351
+ margin: 0,
6352
+ fontSize: "13px",
6353
+ lineHeight: 1.5
6354
+ };
6355
+ const labelStyle = {
6356
+ fontSize: "13px",
6357
+ fontWeight: 600
6358
+ };
6359
+ const controlStyle = {
6360
+ width: "100%",
6361
+ minWidth: 0,
6362
+ boxSizing: "border-box",
6363
+ padding: "9px 11px",
6364
+ borderRadius: "9px",
6365
+ color: "inherit",
6366
+ border: "1px solid color-mix(in srgb, currentColor 24%, transparent)",
6367
+ background: "color-mix(in srgb, currentColor 3%, transparent)",
6368
+ font: "inherit"
6369
+ };
6370
+ const buttonStyle = {
6371
+ padding: "9px 16px",
6372
+ maxWidth: "100%",
6373
+ borderRadius: "999px",
6374
+ cursor: "pointer",
6375
+ whiteSpace: "normal",
6376
+ overflowWrap: "anywhere",
6377
+ font: "inherit",
6378
+ color: "inherit",
6379
+ border: "1px solid color-mix(in srgb, currentColor 30%, transparent)",
6380
+ background: "color-mix(in srgb, currentColor 10%, transparent)"
6381
+ };
6382
+ const styleForButton = (disabled) => ({
6383
+ ...buttonStyle,
6384
+ opacity: disabled ? .55 : 1,
6385
+ cursor: disabled ? "not-allowed" : "pointer"
6386
+ });
6387
+ /** Additive Models settings card. All mutations belong to the injected scoped Remote. */
6388
+ function DualModelCard(props) {
6389
+ const t = props.locale?.toLowerCase().startsWith("zh") ? copy.zh : copy.en;
6390
+ const id = (0, react.useId)();
6391
+ const [view, setView] = (0, react.useState)();
6392
+ const [draft, setDraft] = (0, react.useState)(off);
6393
+ const [workspace, setWorkspace] = (0, react.useState)("");
6394
+ const [phase, setPhase] = (0, react.useState)("loading");
6395
+ const [message, setMessage] = (0, react.useState)();
6396
+ const [needsReload, setNeedsReload] = (0, react.useState)(false);
6397
+ const [pending, setPending] = (0, react.useState)();
6398
+ const lifecycle = (0, react.useRef)({
6399
+ active: false,
6400
+ generation: 0,
6401
+ busy: false
6402
+ });
6403
+ const load = (0, react.useCallback)(async () => {
6404
+ const owner = lifecycle.current;
6405
+ if (!owner.active || owner.busy) return;
6406
+ const generation = ++owner.generation;
6407
+ const current = () => owner.active && owner.generation === generation;
6408
+ owner.busy = true;
6409
+ setPhase("loading");
6410
+ setMessage(void 0);
6411
+ setNeedsReload(true);
6412
+ const creation = creations.get(props.remote);
6413
+ setPending(creation);
6414
+ if (creation) setWorkspace(creation.input.workspaceId);
6415
+ try {
6416
+ const result = await props.remote.view();
6417
+ if (!current()) return;
6418
+ if (!result.ok || !validView(result.value)) {
6419
+ setView(void 0);
6420
+ setMessage(!result.ok ? diagnostic(result.error) ?? "loadError" : "loadError");
6421
+ return;
6422
+ }
6423
+ setView(result.value);
6424
+ setDraft({ ...result.value.configuration });
6425
+ setNeedsReload(false);
6426
+ if (creation) setMessage(creation.sessionId ? "created" : "pending");
6427
+ } catch {
6428
+ if (current()) {
6429
+ setView(void 0);
6430
+ setMessage("loadError");
6431
+ }
6432
+ } finally {
6433
+ if (current()) {
6434
+ owner.busy = false;
6435
+ setPhase("idle");
6436
+ }
6437
+ }
6438
+ }, [props.remote]);
6439
+ (0, react.useEffect)(() => {
6440
+ const owner = lifecycle.current;
6441
+ owner.active = true;
6442
+ setView(void 0);
6443
+ setDraft(off);
6444
+ setWorkspace("");
6445
+ load();
6446
+ return () => {
6447
+ owner.active = false;
6448
+ owner.generation++;
6449
+ owner.busy = false;
6450
+ };
6451
+ }, [load]);
6452
+ const busy = phase !== "idle";
6453
+ const dirty = !!view && !sameConfig(draft, view.configuration);
6454
+ const models = view?.models ?? [];
6455
+ const validModels = models.some((model) => model.id === draft.plannerModel) && models.some((model) => model.id === draft.executorModel);
6456
+ const disabled = busy || !!pending || needsReload || !view?.supported || !view.writable || !validRevision(view.revision);
6457
+ const saveDisabled = disabled || draft.enabled && !validModels;
6458
+ const createDisabled = pending ? busy : disabled || dirty || !draft.enabled || !validModels || !view?.workspaces.some((item) => item.id === workspace);
6459
+ const save = async () => {
6460
+ const owner = lifecycle.current;
6461
+ if (!owner.active || owner.busy || saveDisabled || !view || !validRevision(view.revision)) return;
6462
+ owner.busy = true;
6463
+ const generation = owner.generation;
6464
+ const current = () => owner.active && owner.generation === generation;
6465
+ setPhase("saving");
6466
+ setMessage(void 0);
6467
+ try {
6468
+ const result = await props.remote.save({
6469
+ configuration: { ...draft },
6470
+ expectedRevision: view.revision
6471
+ });
6472
+ if (!current()) return;
6473
+ if (!result.ok || !validView(result.value)) {
6474
+ setNeedsReload(true);
6475
+ setMessage(!result.ok ? diagnostic(result.error) ?? "saveError" : "saveError");
6476
+ return;
6477
+ }
6478
+ setView(result.value);
6479
+ setDraft({ ...result.value.configuration });
6480
+ setMessage("saved");
6481
+ } catch {
6482
+ if (current()) {
6483
+ setNeedsReload(true);
6484
+ setMessage("saveError");
6485
+ }
6486
+ } finally {
6487
+ if (current()) {
6488
+ owner.busy = false;
6489
+ setPhase("idle");
6490
+ }
6491
+ }
6492
+ };
6493
+ const create = async () => {
6494
+ const owner = lifecycle.current;
6495
+ if (!owner.active || owner.busy || createDisabled) return;
6496
+ let creation = creations.get(props.remote);
6497
+ if (!creation) {
6498
+ if (!view || !validRevision(view.revision)) return;
6499
+ try {
6500
+ creation = { input: {
6501
+ requestId: globalThis.crypto.randomUUID(),
6502
+ workspaceId: workspace,
6503
+ expectedRevision: view.revision
6504
+ } };
6505
+ } catch {
6506
+ setMessage("requestUnavailable");
6507
+ return;
6508
+ }
6509
+ creations.set(props.remote, creation);
6510
+ }
6511
+ const request = creation;
6512
+ const generation = owner.generation;
6513
+ const current = () => owner.active && owner.generation === generation;
6514
+ owner.busy = true;
6515
+ setPending(request);
6516
+ setPhase("creating");
6517
+ setMessage(void 0);
6518
+ if (!request.sessionId && !request.flight) request.flight = Promise.resolve().then(() => props.remote.create({ ...request.input })).catch(() => ({
6519
+ ok: false,
6520
+ error: void 0
6521
+ })).then((result) => {
6522
+ request.flight = void 0;
6523
+ if (result?.ok === true && typeof result.value?.sessionId === "string" && result.value.sessionId.length > 0) {
6524
+ request.sessionId = result.value.sessionId;
6525
+ return {
6526
+ ok: true,
6527
+ value: { sessionId: request.sessionId }
6528
+ };
6529
+ }
6530
+ return {
6531
+ ok: false,
6532
+ error: result?.ok === false ? result.error : void 0
6533
+ };
6534
+ });
6535
+ const result = request.sessionId ? {
6536
+ ok: true,
6537
+ value: { sessionId: request.sessionId }
6538
+ } : await request.flight;
6539
+ if (!current()) return;
6540
+ owner.busy = false;
6541
+ setPhase("idle");
6542
+ if (result.ok && typeof result.value?.sessionId === "string" && result.value.sessionId.length > 0) {
6543
+ creations.delete(props.remote);
6544
+ setPending(void 0);
6545
+ setMessage("created");
6546
+ try {
6547
+ props.onOpenSession?.(result.value.sessionId);
6548
+ } catch {
6549
+ setMessage("openError");
6550
+ }
6551
+ } else {
6552
+ const notCreated = !result.ok ? notCreatedDiagnostic(result.error) : void 0;
6553
+ if (notCreated) {
6554
+ creations.delete(props.remote);
6555
+ setPending(void 0);
6556
+ setNeedsReload(true);
6557
+ setMessage(notCreated);
6558
+ } else setMessage("uncertain");
6559
+ }
6560
+ };
6561
+ const field = (role, label, value) => {
6562
+ const missing = value !== "" && !models.some((model) => model.id === value);
6563
+ return (0, react.createElement)("div", { style: fieldStyle }, (0, react.createElement)("label", {
6564
+ htmlFor: `${id}-${role}`,
6565
+ style: labelStyle
6566
+ }, label), (0, react.createElement)("select", {
6567
+ id: `${id}-${role}`,
6568
+ style: controlStyle,
6569
+ value,
6570
+ disabled,
6571
+ [`data-dsh-dual-model-${role}`]: true,
6572
+ "aria-invalid": draft.enabled && !models.some((model) => model.id === value) ? true : void 0,
6573
+ "aria-describedby": `${id}-model-help`,
6574
+ onChange: (event) => {
6575
+ const value = event.currentTarget.value;
6576
+ if (disabled || !models.some((model) => model.id === value)) return;
6577
+ setMessage(void 0);
6578
+ setDraft((current) => ({
6579
+ ...current,
6580
+ [role === "planner" ? "plannerModel" : "executorModel"]: value
6581
+ }));
6582
+ }
6583
+ }, (0, react.createElement)("option", {
6584
+ value: "",
6585
+ disabled: true
6586
+ }, t.selectModel), missing ? (0, react.createElement)("option", {
6587
+ value,
6588
+ disabled: true
6589
+ }, `${value} (${t.unavailable})`) : null, ...models.map((model) => (0, react.createElement)("option", {
6590
+ key: model.id,
6591
+ value: model.id
6592
+ }, model.name || model.id))));
6593
+ };
6594
+ const viewNotice = !view ? void 0 : !view.supported ? diagnostic(view.diagnostic) ?? "unsupported" : !view.writable ? "readonly" : !validRevision(view.revision) ? "revision" : void 0;
6595
+ const status = phase === "loading" ? t.loading : message ? t[message] : "";
6596
+ const shownWorkspace = pending?.input.workspaceId ?? workspace;
6597
+ return (0, react.createElement)("section", {
6598
+ style: cardStyle,
6599
+ "data-dsh-dual-model-card": true,
6600
+ "aria-labelledby": `${id}-title`,
6601
+ "aria-busy": busy
6602
+ }, (0, react.createElement)("div", null, (0, react.createElement)("h3", {
6603
+ id: `${id}-title`,
6604
+ style: {
6605
+ margin: 0,
6606
+ fontSize: "16px"
6607
+ }
6608
+ }, t.title), (0, react.createElement)("p", { style: {
6609
+ ...textStyle,
6610
+ marginTop: "5px"
6611
+ } }, t.description), (0, react.createElement)("p", { style: {
6612
+ ...textStyle,
6613
+ marginTop: "6px"
6614
+ } }, t.scope)), (0, react.createElement)("div", { style: {
6615
+ display: "flex",
6616
+ alignItems: "center",
6617
+ gap: "9px"
6618
+ } }, (0, react.createElement)("input", {
6619
+ type: "checkbox",
6620
+ id: `${id}-enabled`,
6621
+ checked: draft.enabled,
6622
+ disabled,
6623
+ style: {
6624
+ margin: 0,
6625
+ flexShrink: 0
6626
+ },
6627
+ "data-dsh-dual-model-enabled": true,
6628
+ onChange: (event) => {
6629
+ const enabled = event.currentTarget.checked;
6630
+ if (disabled) return;
6631
+ setMessage(void 0);
6632
+ setDraft((current) => ({
6633
+ ...current,
6634
+ enabled
6635
+ }));
6636
+ }
6637
+ }), (0, react.createElement)("label", {
6638
+ htmlFor: `${id}-enabled`,
6639
+ style: labelStyle
6640
+ }, t.enabled)), field("planner", t.planner, draft.plannerModel), field("executor", t.executor, draft.executorModel), (0, react.createElement)("p", {
6641
+ id: `${id}-model-help`,
6642
+ style: textStyle
6643
+ }, t.acceptance, view && draft.enabled && !validModels ? ` ${t.modelsMissing}` : ""), (0, react.createElement)("div", { style: fieldStyle }, (0, react.createElement)("label", {
6644
+ htmlFor: `${id}-workspace`,
6645
+ style: labelStyle
6646
+ }, t.workspace), (0, react.createElement)("select", {
6647
+ id: `${id}-workspace`,
6648
+ style: controlStyle,
6649
+ disabled,
6650
+ value: shownWorkspace,
6651
+ "data-dsh-dual-model-workspace": true,
6652
+ onChange: (event) => {
6653
+ const value = event.currentTarget.value;
6654
+ if (!disabled && view?.workspaces.some((item) => item.id === value)) setWorkspace(value);
6655
+ }
6656
+ }, (0, react.createElement)("option", {
6657
+ value: "",
6658
+ disabled: true
6659
+ }, t.selectWorkspace), shownWorkspace && !view?.workspaces.some((item) => item.id === shownWorkspace) ? (0, react.createElement)("option", {
6660
+ value: shownWorkspace,
6661
+ disabled: true
6662
+ }, `${shownWorkspace} (${t.unavailable})`) : null, ...(view?.workspaces ?? []).map((item) => (0, react.createElement)("option", {
6663
+ key: item.id,
6664
+ value: item.id
6665
+ }, item.name || item.id))), view && view.workspaces.length === 0 ? (0, react.createElement)("p", { style: textStyle }, t.noWorkspaces) : null), viewNotice ? (0, react.createElement)("p", { style: textStyle }, t[viewNotice]) : null, !pending && view && !viewNotice ? (0, react.createElement)("p", { style: textStyle }, dirty ? t.dirty : !draft.enabled ? t.disabled : "") : null, (0, react.createElement)("div", { style: {
6666
+ display: "flex",
6667
+ gap: "12px",
6668
+ flexWrap: "wrap",
6669
+ alignItems: "center",
6670
+ minWidth: 0
6671
+ } }, (0, react.createElement)("button", {
6672
+ type: "button",
6673
+ style: styleForButton(saveDisabled),
6674
+ disabled: saveDisabled,
6675
+ "data-dsh-dual-model-save": true,
6676
+ onClick: () => {
6677
+ save();
6678
+ }
6679
+ }, phase === "saving" ? t.saving : t.save), (0, react.createElement)("button", {
6680
+ type: "button",
6681
+ style: styleForButton(createDisabled),
6682
+ disabled: createDisabled,
6683
+ "data-dsh-dual-model-create": true,
6684
+ onClick: () => {
6685
+ create();
6686
+ }
6687
+ }, phase === "creating" ? t.creating : pending?.sessionId ? t.openCreated : pending ? t.retryCreate : t.create)), (0, react.createElement)("div", {
6688
+ role: "status",
6689
+ "aria-live": "polite",
6690
+ "aria-atomic": true,
6691
+ style: textStyle
6692
+ }, status), (0, react.createElement)("button", {
6693
+ type: "button",
6694
+ style: {
6695
+ ...styleForButton(busy || !!pending),
6696
+ justifySelf: "start"
6697
+ },
6698
+ disabled: busy || !!pending,
6699
+ title: t.reloadTitle,
6700
+ "data-dsh-dual-model-reload": true,
6701
+ onClick: () => {
6702
+ if (!creations.has(props.remote)) load();
6703
+ }
6704
+ }, t.reload));
6705
+ }
6706
+ //#endregion
6707
+ //#region lib/types/dual-model-ui.js
6708
+ const emptySubscribe = () => () => {};
6709
+ function DualModelSurface({ ctx, close }) {
6710
+ const locale = ctx.get("locale");
6711
+ const navigation = ctx.get("uiWorkspace");
6712
+ const language = (0, react.useSyncExternalStore)(locale === void 0 ? emptySubscribe : (listener) => locale.subscribe(listener), () => locale?.getLocale().active ?? "en", () => "en");
6713
+ return (0, react.createElement)(DualModelCard, {
6714
+ remote: ctx.remote.githubCopilotDualModel,
6715
+ locale: language,
6716
+ onOpenSession: navigation === void 0 ? void 0 : (id) => {
6717
+ navigation.openSession(id);
6718
+ close?.();
6719
+ }
6720
+ });
6721
+ }
6722
+ /** A single visible seat under Models, with a section fallback on older Clients. */
6723
+ function registerDualModelUi(ctx) {
6724
+ if (ctx.remote.githubCopilotDualModel === void 0) return () => {};
6725
+ let active = true, footerActive = false, sectionActive = false;
6726
+ let fallback;
6727
+ const syncFallback = () => {
6728
+ if (active && sectionActive && !footerActive) fallback ??= ctx.slots.register({
6729
+ name: "settings.section",
6730
+ id: "github-copilot-dual-model",
6731
+ order: 13,
6732
+ label: "Copilot · Model roles"
6733
+ }, (props) => (0, react.createElement)(DualModelSurface, {
6734
+ ctx,
6735
+ close: props.close
6736
+ }));
6737
+ else {
6738
+ fallback?.();
6739
+ fallback = void 0;
6740
+ }
6741
+ };
6742
+ let footer = () => {};
6743
+ try {
6744
+ footer = ctx.slots.inject("settings.models.footer", () => {
6745
+ try {
6746
+ const spec = ctx.slots.spec?.("settings.models.footer");
6747
+ if (spec?.kind !== "list" || spec.scope !== "root") return () => {};
6748
+ const dispose = ctx.slots.register({
6749
+ name: "settings.models.footer",
6750
+ id: "github-copilot-dual-model",
6751
+ order: 15
6752
+ }, () => (0, react.createElement)(DualModelSurface, { ctx }));
6753
+ footerActive = true;
6754
+ syncFallback();
6755
+ let removed = false;
6756
+ return () => {
6757
+ if (removed) return;
6758
+ removed = true;
6759
+ dispose();
6760
+ footerActive = false;
6761
+ syncFallback();
6762
+ };
6763
+ } catch {
6764
+ ctx.logger.warn("[github-copilot] DUAL_MODEL_FOOTER_UNAVAILABLE");
6765
+ return () => {};
6766
+ }
6767
+ });
6768
+ } catch {
6769
+ ctx.logger.warn("[github-copilot] DUAL_MODEL_FOOTER_UNAVAILABLE");
6770
+ }
6771
+ const section = ctx.slots.inject("settings.section", () => {
6772
+ sectionActive = true;
6773
+ syncFallback();
6774
+ return () => {
6775
+ sectionActive = false;
6776
+ syncFallback();
6777
+ };
6778
+ });
6779
+ return () => {
6780
+ active = false;
6781
+ footer();
6782
+ section();
6783
+ fallback?.();
6784
+ fallback = void 0;
6785
+ };
6786
+ }
6787
+ //#endregion
6112
6788
  //#region lib/types/client.js
6113
6789
  /**
6114
6790
  * Browser companion for the Models provider-card slot.
@@ -7112,6 +7788,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7112
7788
  "remote.settings",
7113
7789
  "slots"
7114
7790
  ], registerSearchUi);
7791
+ const dualModelUi = ctx.inject(["remote.githubCopilotDualModel", "slots"], registerDualModelUi);
7115
7792
  const presentation = ctx.inject(["uiConversation", "slots"], (scope) => installReasoningPresentation({
7116
7793
  slots: scope.slots,
7117
7794
  uiConversation: scope.get("uiConversation"),
@@ -7119,12 +7796,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7119
7796
  }));
7120
7797
  return async () => {
7121
7798
  await presentation.dispose();
7799
+ await dualModelUi.dispose();
7122
7800
  await searchUi.dispose();
7123
7801
  await ui.dispose();
7124
7802
  await disposeRemote();
7125
7803
  };
7126
7804
  }
7127
7805
  //#endregion
7806
+ exports.DualModelCard = DualModelCard;
7128
7807
  exports.GitHubCopilotAccountModelsPanel = GitHubCopilotAccountModelsPanel;
7129
7808
  exports.GitHubCopilotAccountModelsSummary = GitHubCopilotAccountModelsSummary;
7130
7809
  exports.GitHubCopilotAccountModelsUpdatedAt = GitHubCopilotAccountModelsUpdatedAt;