dsh-model-switch 0.4.22 → 0.4.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/PRODUCT.md +7 -6
- package/README.md +43 -15
- package/README.zh.md +43 -15
- package/cordis.patch.yml +15 -1
- package/docs/screenshots/settings-subagent.png +0 -0
- package/lib/auto-review.js +8 -0
- package/lib/client.js +510 -810
- package/lib/compaction.js +51 -18
- package/lib/index.js +25 -96
- package/lib/provider-group-CsZi7H29.js +94 -0
- package/lib/session-route-h9SAqajc.js +172 -0
- package/lib/session-title.js +217 -0
- package/lib/subagent-runtime.js +20 -17
- package/lib/types/auto-review.d.ts +8 -0
- package/lib/types/client/ModelSwitchSettings.d.ts +9 -6
- package/lib/types/client/derived-config-form.d.ts +14 -0
- package/lib/types/client/locales.d.ts +10 -9
- package/lib/types/client/main-row-controller.d.ts +16 -35
- package/lib/types/client/picker/ComposerPicker.d.ts +1 -3
- package/lib/types/client/picker/PlanReviewCard.d.ts +0 -6
- package/lib/types/client/picker/locales.d.ts +0 -8
- package/lib/types/client/picker/provider-group-watch.d.ts +48 -0
- package/lib/types/client-contract.d.ts +8 -9
- package/lib/types/compaction.d.ts +25 -4
- package/lib/types/host-runtime.d.ts +1 -11
- package/lib/types/host-settings.d.ts +6 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/picker/plan-review.d.ts +2 -10
- package/lib/types/provider-group.d.ts +20 -36
- package/lib/types/session-route.d.ts +111 -0
- package/lib/types/session-title.d.ts +104 -0
- package/lib/types/subagent-runtime.d.ts +14 -3
- package/package.json +24 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { n as sessionProviderOf, t as resolveCapabilityRoute } from "./session-route-h9SAqajc.js";
|
|
2
|
+
//#region lib/types/session-title.js
|
|
3
|
+
/**
|
|
4
|
+
* Model Switch-owned session-naming row.
|
|
5
|
+
*
|
|
6
|
+
* The official naming rows hardwire the naming route at registration time: the
|
|
7
|
+
* configured provider/model pair wins over the session's own logged route for
|
|
8
|
+
* every session in the profile. This row registers ONE provider of its own and
|
|
9
|
+
* resolves the route PER REQUEST instead, so a session whose provider has a
|
|
10
|
+
* saved group is named through that group's pair while every other session keeps
|
|
11
|
+
* following its own model.
|
|
12
|
+
*
|
|
13
|
+
* The call itself stays inside the official implementation: the exported
|
|
14
|
+
* `generateSessionTitleWithLlm` receives the session-resolved config and records
|
|
15
|
+
* exactly that route in `session/title-llm-request`, so the durable record and
|
|
16
|
+
* the actual request can never disagree. Nothing is remounted when settings
|
|
17
|
+
* change — the route is read through on every naming request — so a settings
|
|
18
|
+
* save can neither interrupt an in-flight naming request nor leave the profile
|
|
19
|
+
* without a registered provider.
|
|
20
|
+
*
|
|
21
|
+
* `cordis.patch.yml` disables the official row (the seam allows one registered
|
|
22
|
+
* provider per context), and this row mirrors the cadence the disabled row
|
|
23
|
+
* declared, so a profile that restated the row as all-prompts keeps its cadence.
|
|
24
|
+
*
|
|
25
|
+
* @module dsh-model-switch/session-title
|
|
26
|
+
*/
|
|
27
|
+
var __rewriteRelativeImportExtension = function(path, preserveJsx) {
|
|
28
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
|
|
29
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
|
|
30
|
+
});
|
|
31
|
+
return path;
|
|
32
|
+
};
|
|
33
|
+
/** Official session-title provider row this module supersedes. */
|
|
34
|
+
const OFFICIAL_TITLE_ROW = "session-title-llm";
|
|
35
|
+
/** The auxiliary-LLM package carrying the shared naming policy and call. */
|
|
36
|
+
const TITLE_LLM_PACKAGE = "@deepseek-ai/dsh-session-title-llm";
|
|
37
|
+
/** Cordis plugin identity; matches the cordis.patch.yml insert row id. */
|
|
38
|
+
const name = "model-switch-session-title";
|
|
39
|
+
/** The loader names the disabled row, and the seam services the call needs are awaited before the first naming request. */
|
|
40
|
+
const inject = [
|
|
41
|
+
"loader",
|
|
42
|
+
"sessionTitle",
|
|
43
|
+
"llm"
|
|
44
|
+
];
|
|
45
|
+
/** Both official cadence packages, in the order a profile may declare them. */
|
|
46
|
+
const TITLE_CADENCES = Object.freeze([Object.freeze({
|
|
47
|
+
pkg: "@deepseek-ai/dsh-session-title-first-prompt-llm",
|
|
48
|
+
id: "session-title-first-prompt-llm",
|
|
49
|
+
automatic: "first-prompt",
|
|
50
|
+
firstPromptOnly: true
|
|
51
|
+
}), Object.freeze({
|
|
52
|
+
pkg: "@deepseek-ai/dsh-session-title-all-prompts-llm",
|
|
53
|
+
id: "session-title-all-prompts-llm",
|
|
54
|
+
automatic: "all-prompts",
|
|
55
|
+
firstPromptOnly: false
|
|
56
|
+
})]);
|
|
57
|
+
/**
|
|
58
|
+
* The cadence one official package name declares.
|
|
59
|
+
* @param pkg - the disabled row's package name.
|
|
60
|
+
* @returns The matching cadence, or undefined for a row this row cannot mirror.
|
|
61
|
+
*/
|
|
62
|
+
function titleCadence(pkg) {
|
|
63
|
+
if (pkg === void 0) return void 0;
|
|
64
|
+
return TITLE_CADENCES.find((cadence) => cadence.pkg === pkg);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Overlay the session-resolved route over the row's own validated policy.
|
|
68
|
+
*
|
|
69
|
+
* A resolved route always names both halves. When nothing resolves, the row's
|
|
70
|
+
* own policy is returned unchanged: an explicit pair the profile configured on
|
|
71
|
+
* the official row stays the deployment default, and a row without one keeps
|
|
72
|
+
* following each session's logged route.
|
|
73
|
+
* @param config - policy validated from the disabled official row.
|
|
74
|
+
* @param route - session-resolved naming route, or undefined for the row's own policy.
|
|
75
|
+
* @returns A detached config for one naming request.
|
|
76
|
+
*/
|
|
77
|
+
function composeTitleRoute(config, route) {
|
|
78
|
+
if (route === void 0) return config;
|
|
79
|
+
return {
|
|
80
|
+
...config,
|
|
81
|
+
provider: route.provider,
|
|
82
|
+
model: route.model
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Frame the exact human messages one naming revision represents.
|
|
87
|
+
* @param messages - every eligible human message of the revision.
|
|
88
|
+
* @param firstPromptOnly - cadence flag: true mirrors the first-prompt provider.
|
|
89
|
+
* @returns The selected messages, in order.
|
|
90
|
+
* @throws when a first-prompt cadence receives an empty revision — the official provider's own contract.
|
|
91
|
+
*/
|
|
92
|
+
function selectTitleMessages(messages, firstPromptOnly) {
|
|
93
|
+
if (!firstPromptOnly) return messages;
|
|
94
|
+
const first = messages[0];
|
|
95
|
+
if (first === void 0) throw new Error("first-prompt title provider requires one human message");
|
|
96
|
+
return [first];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Locate the official session-title provider row in the loader tree.
|
|
100
|
+
* Disabled entries stay in the tree, so a name-matched disable still matches.
|
|
101
|
+
* @param loader - the host loader, when one is reachable.
|
|
102
|
+
* @returns The official entry, or undefined when this profile has no such row.
|
|
103
|
+
*/
|
|
104
|
+
function officialTitleEntry(loader) {
|
|
105
|
+
if (!loader) return void 0;
|
|
106
|
+
for (const entry of loader.entries()) {
|
|
107
|
+
if (entry.id === OFFICIAL_TITLE_ROW) return entry;
|
|
108
|
+
const pkg = entry.options?.name;
|
|
109
|
+
if (typeof pkg === "string" && TITLE_CADENCES.some((cadence) => cadence.pkg === pkg)) return entry;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Copy the official row's stored config, ignoring non-object payloads.
|
|
114
|
+
* @param entry - the official session-title provider entry.
|
|
115
|
+
* @returns A plain object spread; missing or invalid configs become `{}`. The
|
|
116
|
+
* result is untrusted: the profile may be hand-edited, so the official policy
|
|
117
|
+
* validator runs over it before any naming request is served.
|
|
118
|
+
*/
|
|
119
|
+
function forwardedConfig(entry) {
|
|
120
|
+
const raw = entry.options?.config;
|
|
121
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) return raw;
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The official package name this row mounts (either cadence).
|
|
126
|
+
* @param entry - the official session-title provider entry.
|
|
127
|
+
* @returns The package name, or undefined when the row declares none.
|
|
128
|
+
*/
|
|
129
|
+
function officialTitlePackage(entry) {
|
|
130
|
+
const pkg = entry.options?.name;
|
|
131
|
+
return typeof pkg === "string" && pkg.trim() !== "" ? pkg : void 0;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Read live Model Switch settings without requiring the host row.
|
|
135
|
+
*
|
|
136
|
+
* The host is read structurally instead of injected so this row keeps mirroring
|
|
137
|
+
* the official naming behavior when the `model-switch` row is disabled: an
|
|
138
|
+
* unreachable host resolves to the row's own policy rather than to a pinned
|
|
139
|
+
* route.
|
|
140
|
+
* @param ctx - this row's context.
|
|
141
|
+
* @returns The settings, or undefined when the host is unavailable.
|
|
142
|
+
*/
|
|
143
|
+
function currentSettings(ctx) {
|
|
144
|
+
try {
|
|
145
|
+
return ctx.get("modelSwitch", false)?.currentSettings();
|
|
146
|
+
} catch {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Execute one naming request through the official implementation.
|
|
152
|
+
*
|
|
153
|
+
* The route is resolved from the session being titled — its provider selects the
|
|
154
|
+
* saved group, and the flat settings pair is the deployment default — and is
|
|
155
|
+
* handed to `generateSessionTitleWithLlm`, which frames the request, records the
|
|
156
|
+
* exact route in `session/title-llm-request`, and validates the result.
|
|
157
|
+
* @param ctx - this row's context, exposing `llm`.
|
|
158
|
+
* @param module - the loaded auxiliary-LLM module.
|
|
159
|
+
* @param plan - the profile's fixed naming policy.
|
|
160
|
+
* @param request - service-owned session, message revision, and cancellation.
|
|
161
|
+
* @returns The official provider result.
|
|
162
|
+
*/
|
|
163
|
+
async function generateTitle(ctx, module, plan, request) {
|
|
164
|
+
const provider = sessionProviderOf(ctx, request.session);
|
|
165
|
+
const route = resolveCapabilityRoute(currentSettings(ctx), provider, "title");
|
|
166
|
+
return module.generateSessionTitleWithLlm(ctx, composeTitleRoute(plan.config, route), request, selectTitleMessages(request.messages, plan.cadence.firstPromptOnly), plan.cadence.id);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Mount the Model Switch session-naming row for this profile.
|
|
170
|
+
*
|
|
171
|
+
* A profile with no official naming row, an unrecognized row package, a missing
|
|
172
|
+
* auxiliary-LLM package, or an invalid stored policy keeps the session-title
|
|
173
|
+
* service's deterministic fallback titles and reports the lost capability
|
|
174
|
+
* explicitly instead of guessing a route. The registration is an effect of this
|
|
175
|
+
* row's fiber, so unloading the profile unregisters the provider.
|
|
176
|
+
* @param ctx - this row's context.
|
|
177
|
+
*/
|
|
178
|
+
async function apply(ctx) {
|
|
179
|
+
const entry = officialTitleEntry(ctx.get("loader", false));
|
|
180
|
+
const cadence = titleCadence(entry === void 0 ? void 0 : officialTitlePackage(entry)) ?? TITLE_CADENCES[0];
|
|
181
|
+
if (cadence === void 0) return;
|
|
182
|
+
if (entry === void 0) ctx.logger.info("model-switch: no instantiated official session-title row to mirror; naming uses the first-prompt cadence");
|
|
183
|
+
/** The displaced row's naming policy, or the official defaults when it is absent. */
|
|
184
|
+
const forwarded = entry === void 0 ? {} : forwardedConfig(entry);
|
|
185
|
+
let module;
|
|
186
|
+
try {
|
|
187
|
+
module = await import(__rewriteRelativeImportExtension(TITLE_LLM_PACKAGE));
|
|
188
|
+
} catch (cause) {
|
|
189
|
+
ctx.logger.error(`model-switch: ${TITLE_LLM_PACKAGE} is unavailable; model-backed session naming cannot run at all`);
|
|
190
|
+
ctx.logger.error(cause);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
let config;
|
|
194
|
+
try {
|
|
195
|
+
config = module.resolveSessionTitleLlmConfig(forwarded);
|
|
196
|
+
} catch (cause) {
|
|
197
|
+
ctx.logger.error("model-switch: the official session-title row has no valid naming policy; naming keeps the deterministic fallback");
|
|
198
|
+
ctx.logger.error(cause);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const plan = {
|
|
202
|
+
cadence,
|
|
203
|
+
config
|
|
204
|
+
};
|
|
205
|
+
try {
|
|
206
|
+
ctx.sessionTitle.register({
|
|
207
|
+
id: cadence.id,
|
|
208
|
+
automatic: cadence.automatic,
|
|
209
|
+
generate: (request) => generateTitle(ctx, module, plan, request)
|
|
210
|
+
});
|
|
211
|
+
} catch (cause) {
|
|
212
|
+
ctx.logger.error("model-switch: another session-title provider is already registered; the configurable naming route is unavailable");
|
|
213
|
+
ctx.logger.error(cause);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { TITLE_CADENCES, apply, composeTitleRoute, forwardedConfig, inject, name, officialTitleEntry, officialTitlePackage, selectTitleMessages, titleCadence };
|
package/lib/subagent-runtime.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { r as sessionSubagentRouteOf } from "./session-route-h9SAqajc.js";
|
|
1
2
|
import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
|
|
2
3
|
import OfficialSubagentRuntime from "@deepseek-ai/dsh-subagent";
|
|
3
4
|
//#region lib/types/subagent-runtime.js
|
|
@@ -20,27 +21,28 @@ function present(value) {
|
|
|
20
21
|
function spawnHasAnyRouteField(options) {
|
|
21
22
|
return present(options?.provider) || present(options?.model) || options?.reasoningEffort !== void 0;
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Inject one resolved route into a spawn that names none.
|
|
26
|
+
*
|
|
27
|
+
* A spawn that already names a provider, model, or effort keeps it: the caller's
|
|
28
|
+
* explicit selection outranks any profile policy. The route itself is derived
|
|
29
|
+
* from the DELEGATING session (its provider selects a saved group, else the flat
|
|
30
|
+
* Subagent card in `fixed` mode), so two sessions on different providers route
|
|
31
|
+
* their own spawns and neither can rewrite the other's policy.
|
|
32
|
+
* @param request - the caller's spawn request.
|
|
33
|
+
* @param route - the delegating session's resolved route, or undefined for official inherit.
|
|
34
|
+
* @returns The request to hand the official runtime.
|
|
35
|
+
*/
|
|
36
|
+
function routeSubagentRequest(request, route) {
|
|
33
37
|
if (spawnHasAnyRouteField(request.agentOptions)) return request;
|
|
34
|
-
if (
|
|
35
|
-
const selected = fixedRoute(settings);
|
|
36
|
-
if (selected === void 0) return request;
|
|
38
|
+
if (route === void 0) return request;
|
|
37
39
|
return {
|
|
38
40
|
...request,
|
|
39
41
|
agentOptions: {
|
|
40
42
|
...request.agentOptions,
|
|
41
|
-
provider:
|
|
42
|
-
model:
|
|
43
|
-
...
|
|
43
|
+
provider: route.provider,
|
|
44
|
+
model: route.model,
|
|
45
|
+
...route.reasoningEffort === void 0 ? {} : { reasoningEffort: ReasoningEffortId(route.reasoningEffort) }
|
|
44
46
|
}
|
|
45
47
|
};
|
|
46
48
|
}
|
|
@@ -170,7 +172,8 @@ var ModelSwitchSubagentRuntime = class extends OfficialSubagentRuntime {
|
|
|
170
172
|
routed(name, request) {
|
|
171
173
|
const provider = this.ctx.subagents.getProvider(name);
|
|
172
174
|
if (provider !== void 0 && provider.capabilities.agentOptions !== true) return request;
|
|
173
|
-
|
|
175
|
+
const modelSwitch = routingSurface(this.ctx);
|
|
176
|
+
return routeSubagentRequest(request, sessionSubagentRouteOf(this.ctx, request.parent.session, modelSwitch.currentSettings()));
|
|
174
177
|
}
|
|
175
178
|
start(name, request) {
|
|
176
179
|
return super.start(name, this.routed(name, request));
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
* route configured the wrapper passes options through by identity, so the
|
|
10
10
|
* reviewer follows the session model exactly.
|
|
11
11
|
*
|
|
12
|
+
* The reviewer route is deliberately DEPLOYMENT-WIDE. The official gate builds
|
|
13
|
+
* its request with no session id and no purpose and closes over the single
|
|
14
|
+
* context it was applied with, so this plugin has no public way to tell which
|
|
15
|
+
* session a review serves; guessing one would attribute a review to the wrong
|
|
16
|
+
* session and make the recorded reviewer route false. The unsupported
|
|
17
|
+
* per-session reviewer route is recorded in `docs/seam-gap.md` with the public
|
|
18
|
+
* seam it would need.
|
|
19
|
+
*
|
|
12
20
|
* The official gate stays an opt-in capability: the row mounts it only when
|
|
13
21
|
* the profile actually started with the experimental-auto-review bundle.
|
|
14
22
|
* `cordis.patch.yml` disables the official row (one `registerAuto` per
|
|
@@ -2,27 +2,30 @@ import { type ReactNode } from 'react';
|
|
|
2
2
|
import type { ModelProviderGroup } from '@deepseek-ai/dsh-api-session-controller/types';
|
|
3
3
|
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
4
4
|
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
5
|
-
import { type CapabilityRouteView, type
|
|
5
|
+
import { type CapabilityRouteView, type ModelSwitchSettingsView, type SubagentSettingsView } from '../client-contract.js';
|
|
6
6
|
import { type ProviderGroupConfig } from '../provider-group.js';
|
|
7
7
|
import type { RuntimeCapabilities } from '../runtime-capabilities.js';
|
|
8
8
|
import { type CapabilitiesSnapshot } from './search-capabilities.js';
|
|
9
9
|
import type { ModelSwitchLocaleKey } from './locales.js';
|
|
10
|
-
import type { ConfigViewForm } from './derived-config-form.js';
|
|
10
|
+
import type { ConfigViewForm, ConfigViewOp } from './derived-config-form.js';
|
|
11
11
|
export interface ModelSwitchSettingsFace {
|
|
12
12
|
t: (key: ModelSwitchLocaleKey) => string;
|
|
13
13
|
hooks: {
|
|
14
|
-
mainSettings: ConfigViewForm<MainSettingsView>;
|
|
15
14
|
subagentSettings: ConfigViewForm<SubagentSettingsView>;
|
|
16
15
|
searchSettings: ConfigViewForm<CapabilityRouteView>;
|
|
17
16
|
imageSettings: ConfigViewForm<CapabilityRouteView>;
|
|
18
17
|
autoReviewSettings: ConfigViewForm<CapabilityRouteView>;
|
|
19
18
|
compactionSettings: ConfigViewForm<CapabilityRouteView>;
|
|
19
|
+
titleSettings: ConfigViewForm<CapabilityRouteView>;
|
|
20
20
|
switchSettings: Pick<ConfigForm<ModelSwitchSettingsView>, 'getSnapshot' | 'subscribe'>;
|
|
21
21
|
};
|
|
22
22
|
capabilities: RuntimeCapabilities;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Apply one row's changes as a SINGLE revision-fenced write, so a refused
|
|
25
|
+
* write can never leave a half-written provider/model pair behind.
|
|
26
|
+
*/
|
|
27
|
+
saveSubagent: (ops: readonly ConfigViewOp<SubagentSettingsView>[]) => Promise<void>;
|
|
28
|
+
saveCapability: (route: 'search' | 'image' | 'autoReview' | 'compaction' | 'title', ops: readonly ConfigViewOp<CapabilityRouteView>[]) => Promise<void>;
|
|
26
29
|
setCompactOnSwitch: (value: boolean) => Promise<void>;
|
|
27
30
|
/** Replace or remove (undefined) one provider's saved capability group. */
|
|
28
31
|
saveProviderGroup: (provider: string, group: ProviderGroupConfig | undefined) => Promise<void>;
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import type { ConfigForm, ConfigFormSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
2
|
+
/** One field-level change expressed against a view model, before source-path mapping. */
|
|
3
|
+
export interface ConfigViewOp<T> {
|
|
4
|
+
field: keyof T & string;
|
|
5
|
+
value?: unknown;
|
|
6
|
+
}
|
|
2
7
|
export interface ConfigViewForm<T> {
|
|
3
8
|
getSnapshot(): ConfigFormSnapshot<T>;
|
|
4
9
|
subscribe(listener: () => void): () => void;
|
|
5
10
|
set(field: keyof T & string, value: unknown): Promise<void>;
|
|
6
11
|
unset(field: keyof T & string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Apply several view-field changes as ONE revision-fenced source write, so a row save
|
|
14
|
+
* either lands completely or is rejected as a whole instead of leaving a half-written pair.
|
|
15
|
+
*
|
|
16
|
+
* @param ops view fields to change; `value === undefined` unsets the source field
|
|
17
|
+
* @param expectedRevision revision the draft was seeded from; forwarded to the source fence
|
|
18
|
+
* @returns a promise that rejects with `settings-rejected` when the host refuses the write
|
|
19
|
+
*/
|
|
20
|
+
mutate(ops: readonly ConfigViewOp<T>[], expectedRevision?: number): Promise<void>;
|
|
7
21
|
}
|
|
8
22
|
export declare function deriveConfigForm<Source, View>(source: ConfigForm<Source>, project: (value: Source) => View, fields: {
|
|
9
23
|
[K in keyof View & string]: keyof Source & string;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
export declare const zh: {
|
|
2
2
|
readonly nav: "模型切换";
|
|
3
3
|
readonly title: "模型切换";
|
|
4
|
-
readonly subtitle: "
|
|
4
|
+
readonly subtitle: "为子代理与能力路由设置固定模型;当前会话模型用输入框旁的选择器直接切换。";
|
|
5
5
|
readonly conversationRoutes: "对话路由";
|
|
6
6
|
readonly capabilityRoutes: "能力路由";
|
|
7
7
|
readonly sendProtection: "发送保护";
|
|
8
8
|
readonly settingsSynced: "设置已同步";
|
|
9
9
|
readonly remoteSettingsUnavailable: "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面打开设置。";
|
|
10
|
-
readonly defaultBadge: "默认";
|
|
11
10
|
readonly cancel: "取消";
|
|
12
11
|
readonly compactOnSwitch: "切换时压缩";
|
|
13
12
|
readonly compactOnSwitchHelp: "发送且换了模型时,先按目标窗口检查上下文;超了就用切换前的模型压一次历史,再继续请求。只选模型、不发送,不会压缩。官方自动压缩和 /compact 不受影响。压缩会产生额外模型用量,摘要可能省略细节。";
|
|
14
|
-
readonly main: "主模型";
|
|
15
13
|
readonly provider: "提供商";
|
|
16
14
|
readonly model: "模型";
|
|
17
15
|
readonly providerDefault: "使用提供商默认值";
|
|
18
16
|
readonly providerDefaultShort: "Provider 默认";
|
|
19
17
|
readonly effort: "推理强度";
|
|
18
|
+
readonly effortPinned: "已固定";
|
|
20
19
|
readonly save: "保存";
|
|
21
20
|
readonly saving: "保存中…";
|
|
22
21
|
readonly saved: "已保存";
|
|
@@ -30,10 +29,8 @@ export declare const zh: {
|
|
|
30
29
|
readonly imageHelp: "统一 generate_image 会调用所选 Codex 或 Grok Adapter;Provider 原有图片工具仍保留。";
|
|
31
30
|
readonly unavailable: "未接入";
|
|
32
31
|
readonly loading: "正在加载设置…";
|
|
33
|
-
readonly readonly: "设置为只读";
|
|
34
32
|
readonly requestFailed: "保存失败";
|
|
35
33
|
readonly catalogFailed: "无法加载模型目录。";
|
|
36
|
-
readonly conflict: "设置已在其他位置更改,请检查最新值后重试。";
|
|
37
34
|
readonly autoReview: "自动审查模型";
|
|
38
35
|
readonly autoReviewFollow: "跟随会话模型";
|
|
39
36
|
readonly autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。";
|
|
@@ -42,14 +39,18 @@ export declare const zh: {
|
|
|
42
39
|
readonly compactionFollow: "跟随会话模型";
|
|
43
40
|
readonly compactionHelp: "为官方压缩(/compact 与官方自动压缩)指定摘要模型;关闭(两个字段都留空)时使用官方默认,即当前会话的目标模型。";
|
|
44
41
|
readonly compactionRouteHelp: "摘要请求固定使用这里的 Provider/模型,与会话目标模型无关;保存后对下一次压缩立即生效。保存时若恰有压缩进行中,可能被打断并在下次重试。";
|
|
42
|
+
readonly sessionTitle: "会话命名模型";
|
|
43
|
+
readonly titleFollow: "跟随会话模型";
|
|
44
|
+
readonly titleHelp: "为官方自动命名(新会话首条消息后生成标题)指定模型;关闭(两个字段都留空)时命名跟随当前会话模型。";
|
|
45
|
+
readonly titleRouteHelp: "命名请求固定使用这里的 Provider/模型(不带推理强度);关闭时跟随会话模型。保存后立即对下一次命名生效,不会中断正在进行的命名,会话日志记录的也是实际使用的模型。";
|
|
45
46
|
readonly providerGroups: "供应商配置组";
|
|
46
47
|
readonly providerGroupsCard: "按供应商预设路由";
|
|
47
|
-
readonly providerGroupsSummary: "
|
|
48
|
-
readonly providerGroupsHelp: "
|
|
49
|
-
readonly providerGroupsBodyHelp: "
|
|
48
|
+
readonly providerGroupsSummary: "使用该供应商的会话自动采用对应组";
|
|
49
|
+
readonly providerGroupsHelp: "为会话命名与子代理按供应商分别保存路由。";
|
|
50
|
+
readonly providerGroupsBodyHelp: "每个供应商保存一组会话命名与子代理路由。会话自身的供应商与该组一致时,该会话的命名与子代理按组解析,否则回落到上方卡片里的全局设置。切换某个会话的模型不会写入任何全局设置,因此不同供应商的多个会话互不影响。压缩与自动审查仍是全局卡片:压缩事件要如实记录实际写入摘要的模型,审查的拦截点没有会话身份。";
|
|
50
51
|
readonly providerGroupPick: "选择供应商以编辑其配置组。";
|
|
51
52
|
readonly providerGroupDelete: "删除该组";
|
|
52
|
-
readonly
|
|
53
|
+
readonly saveTimeout: "保存超时,请重试";
|
|
53
54
|
readonly 'reason.central-subagent-routing': "Alpha.4 没有全局子代理启动路由接口。";
|
|
54
55
|
readonly 'reason.packaged-preset-roots': "Alpha.4 不支持插件提供额外 preset root。";
|
|
55
56
|
readonly 'reason.tool-owner-suppression': "Alpha.4 没有工具所有者或来源抑制接口。";
|
|
@@ -1,49 +1,30 @@
|
|
|
1
1
|
import type { ModelProviderGroup } from '@deepseek-ai/dsh-api-session-controller/types';
|
|
2
|
-
import type {
|
|
3
|
-
import { type CapabilityRouteView, type MainSettingsView, type SubagentSettingsView } from '../client-contract.js';
|
|
2
|
+
import type { CapabilityRouteView } from '../client-contract.js';
|
|
4
3
|
import type { ModelSwitchLocaleKey } from './locales.js';
|
|
5
|
-
type Share<T> = (selector: (snapshot: ConfigFormSnapshot<T>) => ConfigFormSnapshot<T>) => ConfigFormSnapshot<T>;
|
|
6
|
-
export interface SettingsControllerInputs {
|
|
7
|
-
useMainSettings: Share<MainSettingsView>;
|
|
8
|
-
useSubagentSettings: Share<SubagentSettingsView>;
|
|
9
|
-
saveMain(next: MainSettingsView, expectedRevision: number): Promise<number>;
|
|
10
|
-
loadCatalog(): Promise<readonly ModelProviderGroup[]>;
|
|
11
|
-
subscribeProviderOrder?: (listener: () => void) => () => void;
|
|
12
|
-
t(key: ModelSwitchLocaleKey): string;
|
|
13
|
-
}
|
|
14
4
|
export interface Choice {
|
|
15
5
|
id: string;
|
|
16
6
|
name: string;
|
|
17
7
|
unavailable?: true;
|
|
18
8
|
}
|
|
9
|
+
/** A concrete provider/model/effort triple; route editors copy these fields. */
|
|
10
|
+
export interface RouteModelSelection {
|
|
11
|
+
provider: string;
|
|
12
|
+
model: string;
|
|
13
|
+
reasoningEffort?: string;
|
|
14
|
+
}
|
|
19
15
|
/** Build a settings route for a newly selected model using only that model's default effort. */
|
|
20
|
-
export declare function selectRouteModel(groups: readonly ModelProviderGroup[], provider: string, model: string):
|
|
16
|
+
export declare function selectRouteModel(groups: readonly ModelProviderGroup[], provider: string, model: string): RouteModelSelection;
|
|
21
17
|
export declare function deriveRouteChoices(groups: readonly ModelProviderGroup[], route: CapabilityRouteView | undefined, allowedProviders?: readonly string[]): {
|
|
22
18
|
providers: Choice[];
|
|
23
19
|
models: Choice[];
|
|
24
20
|
};
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export declare function useModelSwitchSettingsController(input: SettingsControllerInputs): {
|
|
33
|
-
main: ConfigFormSnapshot<MainSettingsView>;
|
|
34
|
-
subagent: ConfigFormSnapshot<SubagentSettingsView>;
|
|
35
|
-
draft: MainSettingsView | undefined;
|
|
21
|
+
export interface SettingsCatalogInputs {
|
|
22
|
+
loadCatalog(): Promise<readonly ModelProviderGroup[]>;
|
|
23
|
+
subscribeProviderOrder?: (listener: () => void) => () => void;
|
|
24
|
+
t(key: ModelSwitchLocaleKey): string;
|
|
25
|
+
}
|
|
26
|
+
/** Load the model catalog backing every route editor, reloading on provider-order changes. */
|
|
27
|
+
export declare function useSettingsCatalog(input: SettingsCatalogInputs): {
|
|
36
28
|
groups: readonly ModelProviderGroup[];
|
|
37
|
-
|
|
38
|
-
models: Choice[];
|
|
39
|
-
efforts: Choice[];
|
|
40
|
-
busy: boolean;
|
|
41
|
-
message: string | undefined;
|
|
42
|
-
disabled: boolean;
|
|
43
|
-
setProvider: (provider: string) => void;
|
|
44
|
-
setModel: (id: string) => void;
|
|
45
|
-
setReasoningEffort: (value: string) => void;
|
|
46
|
-
reset: () => void;
|
|
47
|
-
save: () => Promise<void>;
|
|
29
|
+
catalogError: string | undefined;
|
|
48
30
|
};
|
|
49
|
-
export {};
|
|
@@ -13,8 +13,6 @@ interface ComposerPickerBaseProps {
|
|
|
13
13
|
/** Hide/disable Agent-role groups on existing DSH sessions that are not native-bound. */
|
|
14
14
|
agentLocked?: boolean;
|
|
15
15
|
available: boolean;
|
|
16
|
-
/** Explain a disabled picker when the official Host has no persistent settings channel. */
|
|
17
|
-
unavailableReason?: string;
|
|
18
16
|
directory: PickerDirectoryView;
|
|
19
17
|
t: (key: PickerKey, params?: Record<string, string>) => string;
|
|
20
18
|
embedded?: boolean;
|
|
@@ -44,4 +42,4 @@ export interface ModelPaneHeaderProps {
|
|
|
44
42
|
onQueryChange: (query: string) => void;
|
|
45
43
|
}
|
|
46
44
|
export declare function ModelPaneHeader({ title, backLabel, searchLabel, closeSearchLabel, searchable, searching, query, onBack, onStartSearch, onCloseSearch, onQueryChange, }: ModelPaneHeaderProps): import("react").JSX.Element;
|
|
47
|
-
export declare function ComposerPicker({ locked, providerLock, agentLocked, available,
|
|
45
|
+
export declare function ComposerPicker({ locked, providerLock, agentLocked, available, directory, t, draft, onDraftChange, embedded, tone, resolveInteractionOperations, roleOf, }: ComposerPickerProps): import("react").JSX.Element | null;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { PendingQuestion } from '@deepseek-ai/dsh-client-ui-user-questions/client';
|
|
2
|
-
import type { ConfigFormSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
3
2
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
|
-
import type { MainSettingsView } from '../../client-contract.ts';
|
|
5
3
|
import { type GoalRemote } from '../../picker/plan-goal.ts';
|
|
6
4
|
import type { PlanPreviewBinding } from './plan-preview.ts';
|
|
7
5
|
import { type PickerDirectoryFace } from './PickerDirectory.ts';
|
|
@@ -22,8 +20,6 @@ export interface PlanReviewFace extends PickerDirectoryFace {
|
|
|
22
20
|
};
|
|
23
21
|
/** Re-read the native binding now (mount, turn transitions, pre-selection). */
|
|
24
22
|
refreshProviderLock: () => void;
|
|
25
|
-
subscribeMainDefaults: (listener: () => void) => () => void;
|
|
26
|
-
getMainDefaultsSnapshot: () => ConfigFormSnapshot<MainSettingsView>;
|
|
27
23
|
/** Released goal remote; absent when this deployment mounts no goal service. */
|
|
28
24
|
goalRemote: () => GoalRemote | undefined;
|
|
29
25
|
/** Full-plan sidebar navigation (ui-plan's registered opener). */
|
|
@@ -34,8 +30,6 @@ export interface PlanReviewFace extends PickerDirectoryFace {
|
|
|
34
30
|
export type PlanReviewCardProps = PropsRuntime<'conversation.composer'> & PropsLocale<'composer-picker'> & InjectFace<PlanReviewFace> & {
|
|
35
31
|
matched: PendingQuestion;
|
|
36
32
|
};
|
|
37
|
-
/** Match the model seat's admission guard before letting either picker offer a change. */
|
|
38
|
-
export declare function mainDefaultsUnavailableReason(snapshot: ConfigFormSnapshot<MainSettingsView>, t: PlanReviewCardProps['t']): string | undefined;
|
|
39
33
|
/** Inline failed lock-read status; history and log reading stay unaffected. */
|
|
40
34
|
export declare function ProviderLockHint(props: {
|
|
41
35
|
t: PlanReviewCardProps['t'];
|
|
@@ -20,9 +20,6 @@ export declare const zh: {
|
|
|
20
20
|
'thinking.off': string;
|
|
21
21
|
'effort.providerDefault': string;
|
|
22
22
|
'status.loading': string;
|
|
23
|
-
'settings.remoteUnavailable': string;
|
|
24
|
-
'settings.loading': string;
|
|
25
|
-
'settings.unavailable': string;
|
|
26
23
|
'error.action': string;
|
|
27
24
|
'error.picker': string;
|
|
28
25
|
'action.reload': string;
|
|
@@ -43,7 +40,6 @@ export declare const zh: {
|
|
|
43
40
|
'plan.pickerCrash': string;
|
|
44
41
|
'plan.feedback': string;
|
|
45
42
|
'plan.feedbackPlaceholder': string;
|
|
46
|
-
'plan.notRestored': string;
|
|
47
43
|
'plan.dismissUnsupported': string;
|
|
48
44
|
'plan.goalRun': string;
|
|
49
45
|
'plan.goalFailed': string;
|
|
@@ -77,9 +73,6 @@ export declare const en: {
|
|
|
77
73
|
'thinking.off': string;
|
|
78
74
|
'effort.providerDefault': string;
|
|
79
75
|
'status.loading': string;
|
|
80
|
-
'settings.remoteUnavailable': string;
|
|
81
|
-
'settings.loading': string;
|
|
82
|
-
'settings.unavailable': string;
|
|
83
76
|
'error.action': string;
|
|
84
77
|
'error.picker': string;
|
|
85
78
|
'action.reload': string;
|
|
@@ -100,7 +93,6 @@ export declare const en: {
|
|
|
100
93
|
'plan.pickerCrash': string;
|
|
101
94
|
'plan.feedback': string;
|
|
102
95
|
'plan.feedbackPlaceholder': string;
|
|
103
|
-
'plan.notRestored': string;
|
|
104
96
|
'plan.dismissUnsupported': string;
|
|
105
97
|
'plan.goalRun': string;
|
|
106
98
|
'plan.goalFailed': string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply a provider's saved capability group whenever the seat session's
|
|
3
|
+
* provider changes — on any switch surface.
|
|
4
|
+
*
|
|
5
|
+
* The official model-selection directory projects the Session's durable
|
|
6
|
+
* `model/selection` events into `store.current` (the sole emitter of those
|
|
7
|
+
* events is the `session.selectModel` RPC), so a switch performed through the
|
|
8
|
+
* TUI or any other client lands in the same store this plugin already reads.
|
|
9
|
+
* One watcher therefore covers every surface for sessions open in this Web
|
|
10
|
+
* UI, where the previous wiring only covered this plugin's own two call sites.
|
|
11
|
+
*
|
|
12
|
+
* Semantics stay exactly as before: a provider change applies the target
|
|
13
|
+
* provider's group when one exists; a provider without a group, a same-model
|
|
14
|
+
* change within one provider, and the subscription baseline itself write
|
|
15
|
+
* nothing. A failed apply is reported and never disturbs the switch.
|
|
16
|
+
*/
|
|
17
|
+
import type { ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
|
|
18
|
+
/** The directory store slice the watcher observes. */
|
|
19
|
+
export interface DirectoryWatchSource {
|
|
20
|
+
subscribe: (listener: () => void) => () => void;
|
|
21
|
+
getSnapshot: () => {
|
|
22
|
+
current: ModelSelection | null;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface ProviderGroupWatchOptions {
|
|
26
|
+
source: DirectoryWatchSource;
|
|
27
|
+
/** Adopt the target provider's saved group; a group-less provider must write nothing. */
|
|
28
|
+
apply: (provider: string) => Promise<void>;
|
|
29
|
+
/** Reported apply failure; defaults to a console warning. */
|
|
30
|
+
onFailure?: (error: unknown) => void;
|
|
31
|
+
}
|
|
32
|
+
export interface ProviderGroupWatch {
|
|
33
|
+
/** Detach from the store; in-flight applies are left to finish. */
|
|
34
|
+
dispose: () => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Watch one directory store and apply groups on provider changes.
|
|
38
|
+
*
|
|
39
|
+
* - The first observed provider (or the one already loaded) is the baseline
|
|
40
|
+
* and never triggers a write, so mounting and initial load stay silent.
|
|
41
|
+
* - Applies are serialized: a change arriving mid-apply coalesces to the
|
|
42
|
+
* latest target and runs once the current apply finishes, so two writes
|
|
43
|
+
* never interleave pair writes.
|
|
44
|
+
* - `current` being null (catalog still loading) is ignored, not a change.
|
|
45
|
+
* @param options - the store to watch and the apply to run.
|
|
46
|
+
* @returns the watch handle.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createProviderGroupWatch(options: ProviderGroupWatchOptions): ProviderGroupWatch;
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import type { ProviderGroupsStorage } from './provider-group.js';
|
|
2
2
|
export declare const MODEL_SWITCH_CONFIG_ID = "model-switch";
|
|
3
|
-
export declare const MAIN_DEFAULT_CONFIG_ID = "agent-default-model";
|
|
4
3
|
export declare const PROVIDERS_CONFIG_ID = "llm-providers-ui";
|
|
5
|
-
export interface MainSettingsView {
|
|
6
|
-
provider: string;
|
|
7
|
-
model: string;
|
|
8
|
-
reasoningEffort?: string;
|
|
9
|
-
}
|
|
10
|
-
export declare class MainSettingsConflictError extends Error {
|
|
11
|
-
readonly name = "MainSettingsConflictError";
|
|
12
|
-
}
|
|
13
4
|
export interface ModelSwitchSettingsView {
|
|
14
5
|
subagentMode: 'follow-main' | 'fixed';
|
|
15
6
|
subagentProvider?: string;
|
|
@@ -24,6 +15,8 @@ export interface ModelSwitchSettingsView {
|
|
|
24
15
|
autoReviewModel?: string;
|
|
25
16
|
compactProvider?: string;
|
|
26
17
|
compactModel?: string;
|
|
18
|
+
titleProvider?: string;
|
|
19
|
+
titleModel?: string;
|
|
27
20
|
/** Raw saved per-provider groups; edited whole through the provider-groups card. */
|
|
28
21
|
providerGroups?: ProviderGroupsStorage;
|
|
29
22
|
}
|
|
@@ -63,10 +56,16 @@ export declare const COMPACTION_SETTINGS_FIELDS: Readonly<{
|
|
|
63
56
|
readonly provider: "compactProvider";
|
|
64
57
|
readonly model: "compactModel";
|
|
65
58
|
}>;
|
|
59
|
+
/** Automatic session-title naming route; both unset means the official default (the session's own logged route). */
|
|
60
|
+
export declare const TITLE_SETTINGS_FIELDS: Readonly<{
|
|
61
|
+
readonly provider: "titleProvider";
|
|
62
|
+
readonly model: "titleModel";
|
|
63
|
+
}>;
|
|
66
64
|
export declare function deriveSubagentSettings(settings: ModelSwitchSettingsView): SubagentSettingsView;
|
|
67
65
|
export declare function deriveSearchSettings(settings: ModelSwitchSettingsView): CapabilityRouteView;
|
|
68
66
|
export declare function deriveImageSettings(settings: ModelSwitchSettingsView): CapabilityRouteView;
|
|
69
67
|
export declare function deriveAutoReviewSettings(settings: ModelSwitchSettingsView): CapabilityRouteView;
|
|
70
68
|
export declare function deriveCompactionSettings(settings: ModelSwitchSettingsView): CapabilityRouteView;
|
|
69
|
+
export declare function deriveTitleSettings(settings: ModelSwitchSettingsView): CapabilityRouteView;
|
|
71
70
|
/** Whether both fields of a model route form a complete pair; an incomplete pair stays off. */
|
|
72
71
|
export declare function routePairConfigured(route: CapabilityRouteView | undefined): boolean;
|