dsh-github-copilot 0.4.0-alpha.27 → 0.4.0-alpha.29
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/AGENTS.md +5 -3
- package/CHANGELOG.md +16 -0
- package/README.md +29 -15
- package/README.zh.md +28 -14
- package/deployment-baseline.json +49 -2
- package/docs/copilot-compaction.md +84 -0
- package/docs/model-compatibility-acceptance.md +1 -1
- package/docs/official-first-016-alpha2.md +6 -0
- package/docs/session-search-routing.md +11 -5
- package/lib/client.js +120 -134
- package/lib/client.js.map +1 -1
- package/lib/index.js +393 -88
- package/lib/types/compaction-pressure.d.ts +23 -0
- package/lib/types/config.d.ts +8 -1
- package/lib/types/preview-provider.d.ts +3 -1
- package/lib/types/preview-route.d.ts +3 -0
- package/lib/types/request-budget.d.ts +58 -0
- package/lib/types/web-search-routing-card.d.ts +1 -2
- package/package.json +2 -1
|
@@ -18,6 +18,12 @@ Prefer official implementations when they satisfy the requirement, including con
|
|
|
18
18
|
| Lifecycle/Session seam: initialize policy before queued work; rebuild derived state safely | **Complete for serialized initialization:** official [Agent announcement](https://github.com/deepseek-ai/deepseek-harness/blob/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/core/agent/src/index.ts#L524-L555) awaits `ctx.serial(..., 'agent/created', ...)`. | **Retain** awaited initialization and public projection integration; **retire** the old cache generation via `stateVersion: 2`. | Refold durable events, never add synchronous history reads or rewrite descriptors. Retire plugin lifecycle glue only if official dedicated-policy lifecycle covers creation, resume, cancellation, unload and recovery. Broad exact-source and live acceptance remain unverified. |
|
|
19
19
|
| UI seam: integrated account controls, Models search/role cards and reversible cleanup | **Complete for extension slots:** official [Models section declaration](https://github.com/deepseek-ai/deepseek-harness/blob/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/client/ui-settings-models/src/client/index.ts#L131-L141) exposes keyed/root `settings.models.provider-card` and list/root `settings.models.footer`. Full official parity for the companion workflows is **unverified**. | **Retain** additive contributions and old-Core fallback; preserve native Edit/Delete. | Retire each custom card only when official UI covers its account/discovery or search/role workflow with the same safety, accessibility and lifecycle behavior and passes actual Client/Desktop acceptance. Slot existence is not feature parity and does not authorize hiding or merging real routes. |
|
|
20
20
|
|
|
21
|
+
## Compaction-budget follow-up (#146)
|
|
22
|
+
|
|
23
|
+
Official alpha.2 support is **partial**: [combined model context and adapter purpose policy](https://github.com/deepseek-ai/deepseek-harness/blob/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/llm/llm/src/types.ts) plus [transactional basic compaction](https://github.com/deepseek-ai/deepseek-harness/blob/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/compaction/compaction-basic/src/index.ts) are reused unchanged. Independent Copilot prompt ceilings and full native-request admission remain companion-owned; stock summaries do not fit/chunk an already oversized prefix.
|
|
24
|
+
|
|
25
|
+
**Retain** a metadata-driven estimated budget guard, optional exact-request pressure signalling through stock recovery, and capability-supported low summary effort only when no effort has been resolved. Preserve real catalog capacity, caller output cap, history, cancellation and native transport. No second compaction service, private mutation, automatic model switching or Core patch is added. Retire each companion component when official behavior covers its constraints and passes equivalent exact-runtime regression and migration qualification. [Detailed scope and recovery limitations](./copilot-compaction.md) distinguish prevention from already-oversized-history rescue; the historical alpha.25 evidence below is not qualification of this follow-up.
|
|
26
|
+
|
|
21
27
|
## Descriptor correction is not a history conversion
|
|
22
28
|
|
|
23
29
|
Native `subagent/descriptor` was already **v3 in the retained rc.1 baseline**; the plugin's v1 expectation was a plugin/test bug. Alpha.25 admits the native continuable `spawn` descriptor with explicit `agentProvider`/`agentModel`, and validates its declared native fields. Native descriptor v3, plugin role-policy version, plugin projection cache version and whole-Session storage-format versions are separate concepts.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Requested behavior
|
|
4
4
|
|
|
5
|
-
Issue #135 separates the primary search provider from the final fallback. `github-copilot-search-routing.searchProvider` is `auto` or an exact registered search-provider ID; `defaultSearchProvider` is an independent ID or `none`. Auto follows the initiating Chat provider, never the global default. Copilot's owned aliases retain their existing account/model checks; other Chat IDs must exactly match an observed search registration, without name/suffix/model-family inference. This is a provider-level convention, not a promise of the same execution model or account across independent registries. GPT, Gemini and Grok models do not each become a separate Copilot search provider.
|
|
5
|
+
Issue #135 separates the primary search provider from the final fallback. `github-copilot-search-routing.searchProvider` is `auto` or an exact registered search-provider ID; `defaultSearchProvider` is an independent ID or `none`. Auto follows the initiating Chat provider, never the global default. Copilot's owned aliases retain their existing account/model checks; other Chat IDs must exactly match an observed search registration, without name/suffix/model-family inference. This is a provider-level convention, not a promise of the same execution model or account across independent registries. GPT, Gemini and Grok models do not each become a separate Copilot search provider. Issue #148 makes ordinary settings provider-only: explicit/fallback Copilot resolves account-owned Responses candidates internally unless a nonempty legacy `github-copilot.searchModel` override is present.
|
|
6
6
|
|
|
7
7
|
All implementation belongs to this plugin. No Core source, deployed Core artifact, prototype, private registry, credential copy or global chat-model choice is modified. The original official WebRuntime configuration, including its selected search/fetch providers, is preserved. The routed facade mirrors public search-provider registrations so exact-id selection works for official and community providers without reading Core's private registry.
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ Modern Core may mount `tool-web` in agent presets while disabling its historical
|
|
|
13
13
|
1. An id-and-name-guarded patch moves the existing `web` / `@deepseek-ai/dsh-web` row into the named `github-copilot-original-web` service realm without replacing its config.
|
|
14
14
|
2. The plugin's `web-delegate` entry exposes that same official service through a public, Fiber-owned bridge. It does not read any provider registry internals.
|
|
15
15
|
3. The plugin-owned `routed-web` service occupies the ordinary Host `web` scope. It forwards provider registration and fetch to the original service while retaining Fiber-scoped handles for exact-id search dispatch.
|
|
16
|
-
4. Auto-mode Copilot searches use the captured initiating-session route. Fixed/
|
|
16
|
+
4. Auto-mode Copilot searches use the captured initiating-session route. Fixed/fallback Copilot searches preserve a nonempty explicit provider-owned `searchModel`, otherwise use bounded automatic selection from current account-owned Responses route facts. Other provider ids dispatch through the mirrored public registration. A separate private official WebRuntime supplies native request validation and source capping on every routed branch.
|
|
17
17
|
5. Native `tool-web` consumers, including those in agent presets, retain their original schemas, configured query/source limits, batching, execution policies, timeout metadata, formatting and presentation.
|
|
18
18
|
|
|
19
19
|
This is a plugin-owned subclass and explicit public service composition, not a replacement of Core prototypes or a mutation of a live registered service. It affects all `ctx.web.search` consumers with eligible Copilot context, not only one tool name. The bundle requires the standard enabled top-level official Host `web` row, static provider config and no prior isolation. The patch name guard does not make its separate inserted rows conditional: installing into a custom/missing/disabled/isolated web composition can prevent startup. Run the required read-only `scripts/check-search-composition.mjs` preflight over the existing profile before installing; it uses public readers and composition without calling write-capable `loadProfile` normalization. It compares a routing-free baseline with the actual post-install candidate at the existing bundle position (or the append position for first install), including profile, home and supplied launcher patches. Nonempty disposable `cordis.yml` roots are refused because normal CLI launch resets them; late isolation resets or an original web row inserted only after the routing guard are also refused. `dsh plugin add` does not automatically enforce this step. Unsupported shapes must be refused before mutation, not described as safe no-ops.
|
|
@@ -28,13 +28,19 @@ Fallback notices remain inside canonical `WebSearchResult.content`, so Core resu
|
|
|
28
28
|
|
|
29
29
|
Legacy direct callers without the facade capture seam keep the historical `src/search-routing.ts` Copilot/DeepSeek path only for legacy policy. Its `src/deepseek-search-fallback.ts` constructs the unchanged public official adapter and retains its safe URL validation, exact option snapshot, credential ownership and post-auth/pre-fetch guard. The new catalog-based path instead calls the actual registered backend; an identical ID does not authorize replacing it with that factory. Generic `search()` exposes no internal authentication/pre-network hook, so do not claim the stronger legacy guard for arbitrary third-party implementations.
|
|
30
30
|
|
|
31
|
-
`src/search-routing-policy.ts` normalizes settings without writes: a new explicit `searchProvider` wins; old auto remains Auto; old fixed preserves its default as primary; old fixed plus `none` stays disabled. Unknown saved IDs remain visible and fail closed at dispatch. Until an explicit UI save adopts the new fallback choice, legacy `searchFallback: none` preserves its failure-spending restriction. The save
|
|
31
|
+
`src/search-routing-policy.ts` normalizes settings without writes: a new explicit `searchProvider` wins; old auto remains Auto; old fixed preserves its default as primary; old fixed plus `none` stays disabled. Unknown saved IDs remain visible and fail closed at dispatch. Until an explicit UI save adopts the new fallback choice, legacy `searchFallback: none` preserves its failure-spending restriction. The ordinary save commits both primary and fallback leaves together in one CAS mutation of the routing namespace. It neither requires nor writes the Copilot settings namespace, and it performs no account-status/model-discovery RPC or capability probe. A nonempty legacy `searchModel` remains authoritative and visible read-only; only the separate explicit reset sets `searchModel: ''` with that namespace's own revision. A failed reset must not prevent an unrelated routing save. Missing namespaces, read-only deployments, stale revisions and uncertain responses must remain visible without exposing raw provider errors. `routeWebSearch: false`, the master enable switch, Copilot allowlists and capability proof keep their existing responsibilities.
|
|
32
32
|
|
|
33
33
|
## Search directory and settings UI
|
|
34
34
|
|
|
35
|
-
Both selectors use `githubCopilotSearchRouting.providers()`, a separate strict Remote exposed through the public `TypertRemoteService` binding. Its catalog contains only fresh IDs from registrations observed by this facade, not Core private registry objects or hidden/direct-original registrations. The read invokes no `available()`, auth, discovery or search: availability is contextual and checked on actual use. A missing facade yields `supported: false`, not a static example list. Newly registered IDs appear on reload; removed saved entries remain visibly unavailable. Primary adds Auto;
|
|
35
|
+
Both selectors use `githubCopilotSearchRouting.providers()`, a separate strict Remote exposed through the public `TypertRemoteService` binding. Its catalog contains only fresh IDs from registrations observed by this facade, not Core private registry objects or hidden/direct-original registrations. The read invokes no `available()`, auth, discovery or search: availability is contextual and checked on actual use. A missing facade yields `supported: false`, not a static example list. Newly registered IDs appear on reload; removed saved entries remain visibly unavailable. Primary adds Auto; **Fallback provider** adds None while retaining the stored `defaultSearchProvider` key. The ordinary card has no model selector. Settings and routing Remote faces are captured once at UI registration: real pinned Cordis/Client Gateway namespace reads return fresh traced proxies, so looking them up in each render would change effect dependencies and reload drafts. Models remain provider internals, not additional search-provider entries.
|
|
36
36
|
|
|
37
|
-
Focused postbuild checks must run after `pnpm build`. `tests/search-provider-catalog.spec.ts` checks registration lifecycle, duplicate handling and read purity; `tests/scripts/search-provider-gateway.test.mjs` drives the real Connection Fetch/Gateway against the packaged facade
|
|
37
|
+
Focused postbuild checks must run after `pnpm build`. `tests/search-provider-catalog.spec.ts` checks registration lifecycle, duplicate handling and read purity; `tests/scripts/search-provider-gateway.test.mjs` drives the real Connection Fetch/Gateway against the packaged search-catalog facade/controller. `tests/web-search-routing-card.spec.ts` covers both selectors, unavailable catalogs, one routing CAS without a model prerequisite, explicit separate override reset, safe conflict messages, legacy choices and stale generations. `tests/client-search-inject.spec.ts` mounts the actual Client callback with real React DOM and Cordis in jsdom, checking draft and pending-save continuity through parent rerenders for both footer and legacy section slots; its Remote transport remains synthetic. `tests/scripts/search-settings-contract.test.mjs` uses the real pinned Settings service with in-memory persistence and the unchanged generated Client codecs: two-leaf routing writes, stale CAS, writable hidden/empty legacy overrides and the flat returned namespace revision. It also mounts the real pinned Client Gateway/Remote descriptors to verify traced proxy identity, without RPC or streams. It does **not** execute the actual Host SettingsController write through Gateway. An isolated Edge reproduction with the old alpha.28 built card and actual registration callback, backed by synthetic fresh Remote getters, reproduced both a filled-model draft resetting on a parent render and a completed model write whose subsequent routing write was abandoned on a parent render. This establishes a reproducible plugin lifecycle defect, not a definitive diagnosis of the user's production runtime. The corrected built card passed the same isolated Edge rerender/save/reload flow without a model field or account-status RPC, plus explicit legacy override reset, safe CAS conflict recovery, read-only state, dark/light desktop and 375px mobile captures, and a 200% zoom overflow check. These fixtures are not live account, installed Desktop or paid model evidence. The final local `pnpm verify` passed with one test worker: 62 Vitest files, 1553 passing tests and 2 expected skips, 273 tooling tests, source/test typechecks, baseline checks, build and package smoke. An earlier unrestricted parallel run hit three import-time test timeouts; the unchanged tests passed with bounded concurrency, without extending timeouts or skipping assertions. Remote CI, release publication and loaded-runtime acceptance remain separate pending checks.
|
|
38
|
+
|
|
39
|
+
## Provider-owned automatic Copilot selection (#148)
|
|
40
|
+
|
|
41
|
+
For explicit/fallback Copilot without a nonempty `searchModel`, an actual search non-forcing ensures current account metadata under the existing ownership, TTL, cooldown and credential guards. Eligible account-owned Responses routes are considered in deterministic model-ID order, with at most three candidates. No static model-name table, another Session's model, global Chat default or UI model list supplies the selection. A nonempty override remains the sole candidate and is not silently replaced if unavailable.
|
|
42
|
+
|
|
43
|
+
Metadata determines eligibility, not native-search support. With the default `probe: true`, bounded candidate proof must succeed before one final user-query request is sent. Candidate proof is not a loop replaying the user's query; a final request failure is not retried on another model. The explicit `probe: false` trust override retains its existing meaning and is not a recovery recommendation. Empty/incomplete account evidence, unsupported protocols and proof exhaustion fail closed; cancellation or captured account-proof invalidation terminates the operation. Auto following a Copilot Chat route continues to use the captured initiating model rather than this independent selection. Existing outer primary/final-fallback policy, no-repeat backend rule and charge disclosures remain unchanged. Reusable successful proofs retain only account/owner lifetime, not the creating request's deadline or cancellation. Concurrent configured callers own separate unfinished probes; canceling one must not abort another, and a canceled probe is never reused as negative capability evidence.
|
|
38
44
|
|
|
39
45
|
## Review hardening
|
|
40
46
|
|
package/lib/client.js
CHANGED
|
@@ -5927,7 +5927,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5927
5927
|
//#region lib/types/web-search-routing-card.js
|
|
5928
5928
|
const GITHUB_COPILOT_SETTINGS_NAMESPACE = "github-copilot";
|
|
5929
5929
|
const WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE = "github-copilot-search-routing";
|
|
5930
|
-
const NO_DEFAULT_SEARCH_PROVIDER = "none";
|
|
5931
5930
|
const GITHUB_COPILOT_SEARCH_PROVIDER = "github-copilot-hosted";
|
|
5932
5931
|
const DEEPSEEK_SEARCH_PROVIDER = "deepseek-official";
|
|
5933
5932
|
const cardStyle$1 = {
|
|
@@ -5945,17 +5944,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5945
5944
|
gap: "6px",
|
|
5946
5945
|
minWidth: 0
|
|
5947
5946
|
};
|
|
5948
|
-
const controlStyle = {
|
|
5949
|
-
width: "100%",
|
|
5950
|
-
minWidth: 0,
|
|
5951
|
-
boxSizing: "border-box",
|
|
5952
|
-
padding: "9px 11px",
|
|
5953
|
-
borderRadius: "9px",
|
|
5954
|
-
color: "inherit",
|
|
5955
|
-
border: "1px solid color-mix(in srgb, currentColor 24%, transparent)",
|
|
5956
|
-
background: "color-mix(in srgb, currentColor 3%, transparent)",
|
|
5957
|
-
font: "inherit"
|
|
5958
|
-
};
|
|
5959
5947
|
const buttonStyle$1 = {
|
|
5960
5948
|
justifySelf: "start",
|
|
5961
5949
|
padding: "9px 16px",
|
|
@@ -5970,7 +5958,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5970
5958
|
opacity: .8,
|
|
5971
5959
|
fontSize: "12px",
|
|
5972
5960
|
lineHeight: 1.5,
|
|
5973
|
-
overflowWrap: "anywhere"
|
|
5961
|
+
overflowWrap: "anywhere",
|
|
5962
|
+
margin: 0
|
|
5974
5963
|
};
|
|
5975
5964
|
function record(value) {
|
|
5976
5965
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
@@ -5978,22 +5967,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5978
5967
|
function revisionOf(value) {
|
|
5979
5968
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
5980
5969
|
}
|
|
5981
|
-
|
|
5970
|
+
function providerLabel(id) {
|
|
5971
|
+
return id === GITHUB_COPILOT_SEARCH_PROVIDER ? "GitHub Copilot" : id === DEEPSEEK_SEARCH_PROVIDER ? "DeepSeek" : id;
|
|
5972
|
+
}
|
|
5973
|
+
function saveFailure(error) {
|
|
5974
|
+
const code = record(error).code;
|
|
5975
|
+
if (code === "settings-conflict") return "Search settings changed since you opened this page. Reload settings, then review and save your choices again.";
|
|
5976
|
+
if (code === "settings-rejected") return "The settings service rejected this change. Reload settings to check whether editing is available, then try again.";
|
|
5977
|
+
return "Could not confirm the save. Reload settings to check saved values before retrying. Your current choices have been kept.";
|
|
5978
|
+
}
|
|
5979
|
+
/** Save provider routing independently of account discovery and provider-specific model settings. */
|
|
5982
5980
|
function WebSearchRoutingCard(props) {
|
|
5983
5981
|
const [draft, setDraft] = (0, react.useState)({
|
|
5984
5982
|
primary: "auto",
|
|
5985
5983
|
provider: DEEPSEEK_SEARCH_PROVIDER,
|
|
5986
|
-
model: "",
|
|
5987
5984
|
legacy: true
|
|
5988
5985
|
});
|
|
5989
|
-
const [
|
|
5986
|
+
const [override, setOverride] = (0, react.useState)({ model: "" });
|
|
5990
5987
|
const [providers, setProviders] = (0, react.useState)([]);
|
|
5991
5988
|
const [catalogReady, setCatalogReady] = (0, react.useState)(false);
|
|
5992
5989
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
5993
|
-
const [saving, setSaving] = (0, react.useState)(
|
|
5990
|
+
const [saving, setSaving] = (0, react.useState)();
|
|
5994
5991
|
const [writable, setWritable] = (0, react.useState)(false);
|
|
5995
5992
|
const [message, setMessage] = (0, react.useState)("");
|
|
5996
|
-
const [modelNotice, setModelNotice] = (0, react.useState)("");
|
|
5997
5993
|
const lifecycle = (0, react.useRef)({
|
|
5998
5994
|
active: false,
|
|
5999
5995
|
generation: 0,
|
|
@@ -6006,29 +6002,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6006
6002
|
const current = () => owner.active && owner.generation === generation;
|
|
6007
6003
|
owner.busy = true;
|
|
6008
6004
|
setLoading(true);
|
|
6009
|
-
setSaving(
|
|
6005
|
+
setSaving(void 0);
|
|
6010
6006
|
setWritable(false);
|
|
6011
6007
|
setCatalogReady(false);
|
|
6012
6008
|
setMessage("");
|
|
6013
|
-
setModels([]);
|
|
6014
6009
|
setProviders([]);
|
|
6015
|
-
setModelNotice("");
|
|
6016
|
-
const loadModels = async () => {
|
|
6017
|
-
try {
|
|
6018
|
-
const result = await props.copilot.status();
|
|
6019
|
-
if (!current()) return;
|
|
6020
|
-
const parsed = result.ok ? GitHubCopilotAuthorizationViewSchema.safeParse(result.value) : void 0;
|
|
6021
|
-
if (!parsed?.success) {
|
|
6022
|
-
setModelNotice("Model suggestions are unavailable. Enter a model id or reload settings to retry.");
|
|
6023
|
-
return;
|
|
6024
|
-
}
|
|
6025
|
-
const ids = parsed.data.accountModels?.models.filter((item) => item.api === "openai-responses").map((item) => item.id) ?? [];
|
|
6026
|
-
setModels([...new Set(ids)].sort());
|
|
6027
|
-
} catch {
|
|
6028
|
-
if (current()) setModelNotice("Model suggestions are unavailable. Enter a model id or reload settings to retry.");
|
|
6029
|
-
}
|
|
6030
|
-
};
|
|
6031
|
-
loadModels();
|
|
6032
6010
|
try {
|
|
6033
6011
|
const [result, catalog] = await Promise.all([props.settings.describe(), Promise.resolve().then(() => props.routing.providers()).catch(() => void 0)]);
|
|
6034
6012
|
if (!current()) return;
|
|
@@ -6048,9 +6026,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6048
6026
|
primary: normalized.primaryProvider,
|
|
6049
6027
|
provider: normalized.defaultProvider,
|
|
6050
6028
|
legacy: normalized.legacy,
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6029
|
+
routingRevision: revisionOf(routing?.revision)
|
|
6030
|
+
});
|
|
6031
|
+
setOverride({
|
|
6032
|
+
model: typeof copilotValue.searchModel === "string" ? copilotValue.searchModel.trim() : "",
|
|
6033
|
+
revision: revisionOf(copilot?.revision)
|
|
6054
6034
|
});
|
|
6055
6035
|
const parsed = catalog?.ok ? SearchProviderCatalogSchema.safeParse(catalog.value) : void 0;
|
|
6056
6036
|
const ids = parsed?.success ? parsed.data.providers.map((entry) => entry.id) : [];
|
|
@@ -6064,21 +6044,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6064
6044
|
if (!routing || revisionOf(routing.revision) === void 0) setMessage("Web search routing settings are unavailable. Reload after the routing namespace is registered.");
|
|
6065
6045
|
else if (!result.value.writable) setMessage("Search settings are read-only in this deployment.");
|
|
6066
6046
|
} catch {
|
|
6067
|
-
|
|
6047
|
+
if (current()) setMessage("Could not load search settings. Reload settings to retry.");
|
|
6068
6048
|
} finally {
|
|
6069
6049
|
if (current()) {
|
|
6070
6050
|
owner.busy = false;
|
|
6071
6051
|
setLoading(false);
|
|
6072
6052
|
}
|
|
6073
6053
|
}
|
|
6074
|
-
|
|
6075
|
-
if (current()) setMessage(value);
|
|
6076
|
-
}
|
|
6077
|
-
}, [
|
|
6078
|
-
props.copilot,
|
|
6079
|
-
props.settings,
|
|
6080
|
-
props.routing
|
|
6081
|
-
]);
|
|
6054
|
+
}, [props.settings, props.routing]);
|
|
6082
6055
|
(0, react.useEffect)(() => {
|
|
6083
6056
|
const owner = lifecycle.current;
|
|
6084
6057
|
owner.active = true;
|
|
@@ -6090,55 +6063,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6090
6063
|
};
|
|
6091
6064
|
}, [load]);
|
|
6092
6065
|
const usesCopilot = draft.primary === GITHUB_COPILOT_SEARCH_PROVIDER || draft.provider === GITHUB_COPILOT_SEARCH_PROVIDER;
|
|
6093
|
-
const disabled = loading || saving || !writable || draft.routingRevision === void 0 || !catalogReady;
|
|
6094
|
-
const missingCopilot = usesCopilot && draft.copilotRevision === void 0;
|
|
6066
|
+
const disabled = loading || saving !== void 0 || !writable || draft.routingRevision === void 0 || !catalogReady;
|
|
6095
6067
|
const primaryKnown = draft.primary === "auto" || draft.primary === "none" || providers.includes(draft.primary);
|
|
6096
6068
|
const fallbackKnown = draft.provider === "none" || providers.includes(draft.provider);
|
|
6097
6069
|
const missingProvider = !primaryKnown || !fallbackKnown;
|
|
6098
6070
|
const save = (0, react.useCallback)(async () => {
|
|
6099
6071
|
const owner = lifecycle.current;
|
|
6100
|
-
if (!owner.active || owner.busy || disabled
|
|
6072
|
+
if (!owner.active || owner.busy || disabled) return;
|
|
6101
6073
|
if (missingProvider) {
|
|
6102
6074
|
setMessage("Choose a registered search provider, or disable the fallback with None.");
|
|
6103
6075
|
return;
|
|
6104
6076
|
}
|
|
6105
6077
|
const generation = owner.generation;
|
|
6106
6078
|
const current = () => owner.active && owner.generation === generation;
|
|
6107
|
-
const provider = draft.provider, primary = draft.primary
|
|
6108
|
-
if (usesCopilot && model.length === 0) {
|
|
6109
|
-
setMessage("Enter a Copilot Responses model before using GitHub Copilot search.");
|
|
6110
|
-
return;
|
|
6111
|
-
}
|
|
6079
|
+
const provider = draft.provider, primary = draft.primary;
|
|
6112
6080
|
owner.busy = true;
|
|
6113
|
-
setSaving(
|
|
6081
|
+
setSaving("routing");
|
|
6114
6082
|
setMessage("");
|
|
6115
|
-
let modelSaved = false;
|
|
6116
|
-
const saveFailure = () => modelSaved ? "Copilot search model was saved, but routing was not confirmed. Retry Save, or reload settings to review current values. The two namespaces are not saved atomically." : "Search settings could not be saved or confirmed. Retry Save, or reload settings to review current values before retrying.";
|
|
6117
6083
|
try {
|
|
6118
|
-
|
|
6119
|
-
const modelWrite = await props.settings.mutate(GITHUB_COPILOT_SETTINGS_NAMESPACE, [{
|
|
6120
|
-
op: "set",
|
|
6121
|
-
path: ["searchModel"],
|
|
6122
|
-
value: model
|
|
6123
|
-
}], draft.copilotRevision);
|
|
6124
|
-
if (!current()) return;
|
|
6125
|
-
if (!modelWrite.ok) {
|
|
6126
|
-
setMessage(saveFailure());
|
|
6127
|
-
return;
|
|
6128
|
-
}
|
|
6129
|
-
modelSaved = true;
|
|
6130
|
-
const revision = revisionOf(modelWrite.value.revision);
|
|
6131
|
-
setDraft((value) => current() ? {
|
|
6132
|
-
...value,
|
|
6133
|
-
model,
|
|
6134
|
-
copilotRevision: revision
|
|
6135
|
-
} : value);
|
|
6136
|
-
if (revision === void 0) {
|
|
6137
|
-
setMessage(saveFailure());
|
|
6138
|
-
return;
|
|
6139
|
-
}
|
|
6140
|
-
}
|
|
6141
|
-
const routingWrite = await props.settings.mutate(WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE, [{
|
|
6084
|
+
const result = await props.settings.mutate(WEB_SEARCH_ROUTING_SETTINGS_NAMESPACE, [{
|
|
6142
6085
|
op: "set",
|
|
6143
6086
|
path: ["searchProvider"],
|
|
6144
6087
|
value: primary
|
|
@@ -6148,11 +6091,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6148
6091
|
value: provider
|
|
6149
6092
|
}], draft.routingRevision);
|
|
6150
6093
|
if (!current()) return;
|
|
6151
|
-
if (!
|
|
6152
|
-
setMessage(saveFailure());
|
|
6094
|
+
if (!result.ok) {
|
|
6095
|
+
setMessage(saveFailure(result.error));
|
|
6153
6096
|
return;
|
|
6154
6097
|
}
|
|
6155
|
-
const revision = revisionOf(
|
|
6098
|
+
const revision = revisionOf(result.value.revision);
|
|
6156
6099
|
setDraft((value) => current() ? {
|
|
6157
6100
|
...value,
|
|
6158
6101
|
primary,
|
|
@@ -6160,28 +6103,72 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6160
6103
|
routingRevision: revision,
|
|
6161
6104
|
legacy: false
|
|
6162
6105
|
} : value);
|
|
6163
|
-
setMessage(revision === void 0 ? "Save returned no revision. Reload settings before editing again." : "Saved. New searches use this routing
|
|
6164
|
-
} catch {
|
|
6165
|
-
if (current()) setMessage(saveFailure());
|
|
6106
|
+
setMessage(revision === void 0 ? "Save returned no revision. Reload settings before editing again." : "Saved. New searches use this routing. Search availability is checked when you search.");
|
|
6107
|
+
} catch (error) {
|
|
6108
|
+
if (current()) setMessage(saveFailure(error));
|
|
6166
6109
|
} finally {
|
|
6167
6110
|
if (current()) {
|
|
6168
6111
|
owner.busy = false;
|
|
6169
|
-
setSaving(
|
|
6112
|
+
setSaving(void 0);
|
|
6170
6113
|
}
|
|
6171
6114
|
}
|
|
6172
6115
|
}, [
|
|
6173
6116
|
disabled,
|
|
6174
6117
|
draft,
|
|
6175
|
-
missingCopilot,
|
|
6176
6118
|
missingProvider,
|
|
6177
|
-
props.settings
|
|
6178
|
-
|
|
6119
|
+
props.settings
|
|
6120
|
+
]);
|
|
6121
|
+
const clearOverride = (0, react.useCallback)(async () => {
|
|
6122
|
+
const owner = lifecycle.current;
|
|
6123
|
+
if (!owner.active || owner.busy || disabled || override.revision === void 0) return;
|
|
6124
|
+
const generation = owner.generation;
|
|
6125
|
+
const current = () => owner.active && owner.generation === generation;
|
|
6126
|
+
owner.busy = true;
|
|
6127
|
+
setSaving("override");
|
|
6128
|
+
setMessage("");
|
|
6129
|
+
try {
|
|
6130
|
+
const result = await props.settings.mutate(GITHUB_COPILOT_SETTINGS_NAMESPACE, [{
|
|
6131
|
+
op: "set",
|
|
6132
|
+
path: ["searchModel"],
|
|
6133
|
+
value: ""
|
|
6134
|
+
}], override.revision);
|
|
6135
|
+
if (!current()) return;
|
|
6136
|
+
if (!result.ok) {
|
|
6137
|
+
setMessage(saveFailure(result.error));
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
const revision = revisionOf(result.value.revision);
|
|
6141
|
+
if (revision === void 0) {
|
|
6142
|
+
setOverride((value) => ({
|
|
6143
|
+
...value,
|
|
6144
|
+
revision: void 0
|
|
6145
|
+
}));
|
|
6146
|
+
setMessage("Save returned no revision. Reload settings before editing again.");
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
setOverride({
|
|
6150
|
+
model: "",
|
|
6151
|
+
revision
|
|
6152
|
+
});
|
|
6153
|
+
setMessage("Copilot model selection is now automatic. Unsaved provider choices have not been applied.");
|
|
6154
|
+
} catch (error) {
|
|
6155
|
+
if (current()) setMessage(saveFailure(error));
|
|
6156
|
+
} finally {
|
|
6157
|
+
if (current()) {
|
|
6158
|
+
owner.busy = false;
|
|
6159
|
+
setSaving(void 0);
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
}, [
|
|
6163
|
+
disabled,
|
|
6164
|
+
override,
|
|
6165
|
+
props.settings
|
|
6179
6166
|
]);
|
|
6180
6167
|
const providerOptions = () => providers.map((id) => (0, react.createElement)("option", {
|
|
6181
6168
|
key: id,
|
|
6182
6169
|
value: id,
|
|
6183
6170
|
style: nativeOptionStyle()
|
|
6184
|
-
}, id));
|
|
6171
|
+
}, providerLabel(id)));
|
|
6185
6172
|
const unavailableOption = (id, known) => known ? null : (0, react.createElement)("option", {
|
|
6186
6173
|
value: id,
|
|
6187
6174
|
disabled: true,
|
|
@@ -6189,7 +6176,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6189
6176
|
}, `${id || "(empty)"} — unavailable`);
|
|
6190
6177
|
return (0, react.createElement)("section", {
|
|
6191
6178
|
style: cardStyle$1,
|
|
6192
|
-
"data-dsh-web-search-routing": true
|
|
6179
|
+
"data-dsh-web-search-routing": true,
|
|
6180
|
+
"aria-busy": loading || saving !== void 0
|
|
6193
6181
|
}, (0, react.createElement)("div", null, (0, react.createElement)("h3", { style: {
|
|
6194
6182
|
margin: 0,
|
|
6195
6183
|
fontSize: "16px"
|
|
@@ -6198,7 +6186,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6198
6186
|
opacity: .8,
|
|
6199
6187
|
fontSize: "13px",
|
|
6200
6188
|
lineHeight: 1.5
|
|
6201
|
-
} }, "
|
|
6189
|
+
} }, "Choose who runs web searches. This does not change your Chat model.")), (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
|
|
6202
6190
|
fontSize: "13px",
|
|
6203
6191
|
fontWeight: 600
|
|
6204
6192
|
} }, "Search provider"), (0, react.createElement)("select", {
|
|
@@ -6208,6 +6196,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6208
6196
|
"data-dsh-web-search-mode": true,
|
|
6209
6197
|
onChange: (event) => {
|
|
6210
6198
|
const primary = event.currentTarget.value;
|
|
6199
|
+
setMessage("");
|
|
6211
6200
|
setDraft((current) => ({
|
|
6212
6201
|
...current,
|
|
6213
6202
|
primary
|
|
@@ -6219,54 +6208,46 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6219
6208
|
}, "Auto — follow Chat"), draft.primary === "none" ? (0, react.createElement)("option", {
|
|
6220
6209
|
value: "none",
|
|
6221
6210
|
style: nativeOptionStyle()
|
|
6222
|
-
}, "Disabled — saved configuration") : null, ...providerOptions(), unavailableOption(draft.primary, primaryKnown))), (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
|
|
6211
|
+
}, "Disabled — saved configuration") : null, ...providerOptions(), unavailableOption(draft.primary, primaryKnown)), (0, react.createElement)("span", { style: hintStyle }, "Auto follows the Chat provider when it supports search. A selected provider stays fixed when you switch Chat models.")), (0, react.createElement)("label", { style: fieldStyle$1 }, (0, react.createElement)("span", { style: {
|
|
6223
6212
|
fontSize: "13px",
|
|
6224
6213
|
fontWeight: 600
|
|
6225
|
-
} }, "
|
|
6214
|
+
} }, "Fallback provider"), (0, react.createElement)("select", {
|
|
6226
6215
|
style: nativeSelectStyle(disabled),
|
|
6227
6216
|
disabled,
|
|
6228
6217
|
value: draft.provider,
|
|
6229
6218
|
"data-dsh-web-search-provider": true,
|
|
6230
6219
|
onChange: (event) => {
|
|
6231
6220
|
const provider = event.currentTarget.value;
|
|
6221
|
+
setMessage("");
|
|
6232
6222
|
setDraft((current) => ({
|
|
6233
6223
|
...current,
|
|
6234
6224
|
provider
|
|
6235
6225
|
}));
|
|
6236
6226
|
}
|
|
6237
6227
|
}, (0, react.createElement)("option", {
|
|
6238
|
-
value:
|
|
6228
|
+
value: "none",
|
|
6239
6229
|
style: nativeOptionStyle()
|
|
6240
|
-
}, "None — no fallback"), ...providerOptions(), unavailableOption(draft.provider, fallbackKnown)), (0, react.createElement)("span", { style: hintStyle }, "
|
|
6230
|
+
}, "None — no fallback"), ...providerOptions(), unavailableOption(draft.provider, fallbackKnown)), (0, react.createElement)("span", { style: hintStyle }, "Used if Auto has no matching provider or the primary search fails. May incur the fallback provider’s charges. The same provider is not tried twice.")), catalogReady && providers.length === 0 ? (0, react.createElement)("p", {
|
|
6241
6231
|
role: "status",
|
|
6242
6232
|
style: hintStyle
|
|
6243
6233
|
}, "No search providers are registered. Enable a search provider, then reload settings.") : null, catalogReady && missingProvider ? (0, react.createElement)("p", {
|
|
6244
6234
|
role: "status",
|
|
6245
6235
|
style: hintStyle
|
|
6246
|
-
}, "A saved provider is no longer registered. Choose an available entry; it has not been replaced automatically.") : null, draft.legacy && catalogReady ? (0, react.createElement)("p", { style: hintStyle }, "
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
} }, "Copilot
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
...current,
|
|
6260
|
-
model
|
|
6261
|
-
}));
|
|
6236
|
+
}, "A saved provider is no longer registered. Choose an available entry; it has not been replaced automatically.") : null, draft.legacy && catalogReady ? (0, react.createElement)("p", { style: hintStyle }, "Saving adopts the selected fallback for new searches. Cancellation and account-proof invalidation never trigger fallback.") : null, usesCopilot ? (0, react.createElement)("p", { style: hintStyle }, "Copilot handles search model selection. No model setup is needed here. A signed-in account and a model with verified search support are required when searching.") : null, usesCopilot && override.model ? (0, react.createElement)("details", { "data-dsh-copilot-search-override": true }, (0, react.createElement)("summary", { style: {
|
|
6237
|
+
cursor: "pointer",
|
|
6238
|
+
fontSize: "13px"
|
|
6239
|
+
} }, "Existing Copilot model override"), (0, react.createElement)("div", { style: {
|
|
6240
|
+
...fieldStyle$1,
|
|
6241
|
+
marginTop: "8px"
|
|
6242
|
+
} }, (0, react.createElement)("p", { style: hintStyle }, "Your saved override ", (0, react.createElement)("code", null, override.model), " is preserved. Saving providers does not change it."), (0, react.createElement)("button", {
|
|
6243
|
+
type: "button",
|
|
6244
|
+
style: buttonStyle$1,
|
|
6245
|
+
disabled: disabled || override.revision === void 0,
|
|
6246
|
+
"data-dsh-copilot-search-reset": true,
|
|
6247
|
+
onClick: () => {
|
|
6248
|
+
clearOverride();
|
|
6262
6249
|
}
|
|
6263
|
-
}
|
|
6264
|
-
key: id,
|
|
6265
|
-
value: id
|
|
6266
|
-
}))), (0, react.createElement)("span", { style: hintStyle }, "Used when Copilot is selected explicitly or as the fallback. Account Responses models are suggestions, not hosted-search capability proof. This does not change the Chat model."), missingCopilot ? (0, react.createElement)("span", { role: "status" }, "Copilot search settings are unavailable. Reload after the github-copilot namespace is registered, or choose another provider.") : null, modelNotice ? (0, react.createElement)("span", {
|
|
6267
|
-
role: "status",
|
|
6268
|
-
style: hintStyle
|
|
6269
|
-
}, modelNotice) : null), (0, react.createElement)("div", { style: {
|
|
6250
|
+
}, saving === "override" ? "Updating…" : "Use automatic model selection"))) : null, (0, react.createElement)("div", { style: {
|
|
6270
6251
|
display: "flex",
|
|
6271
6252
|
alignItems: "center",
|
|
6272
6253
|
gap: "12px",
|
|
@@ -6274,24 +6255,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6274
6255
|
} }, (0, react.createElement)("button", {
|
|
6275
6256
|
type: "button",
|
|
6276
6257
|
style: buttonStyle$1,
|
|
6277
|
-
disabled: disabled ||
|
|
6258
|
+
disabled: disabled || missingProvider,
|
|
6278
6259
|
"data-dsh-web-search-save": true,
|
|
6279
6260
|
onClick: () => {
|
|
6280
6261
|
save();
|
|
6281
6262
|
}
|
|
6282
|
-
}, loading ? "Loading…" : saving ? "Saving…" : "Save search routing"), (0, react.createElement)("button", {
|
|
6263
|
+
}, loading ? "Loading…" : saving === "routing" ? "Saving…" : "Save search routing"), (0, react.createElement)("button", {
|
|
6283
6264
|
type: "button",
|
|
6284
6265
|
style: buttonStyle$1,
|
|
6285
|
-
disabled: loading || saving,
|
|
6266
|
+
disabled: loading || saving !== void 0,
|
|
6286
6267
|
title: "Reload saved values and discard unsaved edits",
|
|
6287
6268
|
"data-dsh-web-search-reload": true,
|
|
6288
6269
|
onClick: () => {
|
|
6289
6270
|
load();
|
|
6290
6271
|
}
|
|
6291
|
-
}, "Reload settings"),
|
|
6272
|
+
}, "Reload settings")), (0, react.createElement)("p", {
|
|
6292
6273
|
role: "status",
|
|
6293
|
-
|
|
6294
|
-
|
|
6274
|
+
"aria-live": "polite",
|
|
6275
|
+
"aria-atomic": true,
|
|
6276
|
+
style: {
|
|
6277
|
+
...hintStyle,
|
|
6278
|
+
...message ? {} : { display: "none" }
|
|
6279
|
+
}
|
|
6280
|
+
}, message));
|
|
6295
6281
|
}
|
|
6296
6282
|
//#endregion
|
|
6297
6283
|
//#region lib/types/dual-model-card.js
|
|
@@ -7898,10 +7884,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7898
7884
|
let footerActive = false;
|
|
7899
7885
|
let sectionActive = false;
|
|
7900
7886
|
let fallback;
|
|
7887
|
+
const settings = ctx.remote.settings;
|
|
7888
|
+
const routing = ctx.remote.githubCopilotSearchRouting;
|
|
7901
7889
|
const render = () => (0, react.createElement)(WebSearchRoutingCard, {
|
|
7902
|
-
settings
|
|
7903
|
-
|
|
7904
|
-
routing: ctx.remote.githubCopilotSearchRouting
|
|
7890
|
+
settings,
|
|
7891
|
+
routing
|
|
7905
7892
|
});
|
|
7906
7893
|
const syncFallback = () => {
|
|
7907
7894
|
if (active && sectionActive && !footerActive) fallback ??= ctx.slots.register({
|
|
@@ -7972,7 +7959,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7972
7959
|
throw error;
|
|
7973
7960
|
}
|
|
7974
7961
|
const searchUi = ctx.inject([
|
|
7975
|
-
"remote.githubCopilot",
|
|
7976
7962
|
"remote.settings",
|
|
7977
7963
|
"remote.githubCopilotSearchRouting",
|
|
7978
7964
|
"slots"
|