dsh-code 1.0.6 → 1.2.0
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/README.en.md +123 -26
- package/README.md +124 -27
- package/bin/deepseek.mjs +283 -35
- package/cordis.patch.yml +97 -0
- package/lib/index.mjs +5008 -881
- package/lib/session-query.mjs +150 -0
- package/lib/startup.mjs +4 -4
- package/lib/{theme-DCT8Y2xf.mjs → theme-7u5Qo3dF.mjs} +657 -20
- package/lib/types/app.d.ts +106 -62
- package/lib/types/authorization-panel.d.ts +3 -3
- package/lib/types/git-workflow.d.ts +91 -2
- package/lib/types/i18n.d.ts +39 -0
- package/lib/types/index.d.ts +100 -1
- package/lib/types/input-split.d.ts +1 -1
- package/lib/types/kernel-panels.d.ts +107 -29
- package/lib/types/language-panel.d.ts +12 -0
- package/lib/types/locales/en.d.ts +450 -0
- package/lib/types/locales/zh.d.ts +9 -0
- package/lib/types/mentions.d.ts +7 -3
- package/lib/types/models.d.ts +14 -0
- package/lib/types/panel-accent.d.ts +28 -0
- package/lib/types/rainbow.d.ts +69 -0
- package/lib/types/render/animations.d.ts +42 -0
- package/lib/types/render/editor.d.ts +4 -3
- package/lib/types/render/ime-cursor.d.ts +60 -0
- package/lib/types/render/inspector.d.ts +26 -0
- package/lib/types/render/lines.d.ts +21 -1
- package/lib/types/render/markdown.d.ts +1 -1
- package/lib/types/render/projection.d.ts +130 -4
- package/lib/types/render/status.d.ts +9 -9
- package/lib/types/render/text.d.ts +6 -0
- package/lib/types/render/usage.d.ts +113 -0
- package/lib/types/session-directory.d.ts +17 -0
- package/lib/types/session-query.d.ts +92 -0
- package/lib/types/startup.d.ts +1 -1
- package/lib/types/terminal-title.d.ts +66 -0
- package/lib/types/theme-panel.d.ts +2 -2
- package/lib/types/theme.d.ts +271 -52
- package/lib/types/update-panel.d.ts +49 -0
- package/lib/types/update.d.ts +75 -0
- package/lib/types/version.d.ts +4 -3
- package/package.json +246 -90
- package/src/app.ts +1369 -509
- package/src/approval.ts +166 -166
- package/src/authorization-panel.ts +19 -16
- package/src/editor-keys.ts +371 -371
- package/src/git-workflow.ts +229 -3
- package/src/i18n.ts +68 -0
- package/src/index.ts +534 -80
- package/src/input-split.ts +3 -3
- package/src/internals.ts +5 -0
- package/src/kernel-panels.ts +554 -86
- package/src/keyboard.ts +5 -4
- package/src/language-panel.ts +53 -0
- package/src/locales/en.ts +489 -0
- package/src/locales/zh.ts +488 -0
- package/src/mentions.ts +8 -4
- package/src/models.ts +264 -212
- package/src/panel-accent.ts +41 -0
- package/src/presets.ts +1 -1
- package/src/provider-settings.ts +1 -1
- package/src/rainbow.ts +208 -0
- package/src/render/animations.ts +104 -6
- package/src/render/editor.ts +25 -24
- package/src/render/export.ts +116 -95
- package/src/render/ime-cursor.ts +147 -0
- package/src/render/inspector.ts +42 -0
- package/src/render/lines.ts +628 -415
- package/src/render/markdown.ts +15 -3
- package/src/render/projection.ts +572 -21
- package/src/render/status.ts +59 -39
- package/src/render/text.ts +14 -0
- package/src/render/tool-preview.ts +77 -77
- package/src/render/usage.ts +430 -0
- package/src/render/width.ts +2 -2
- package/src/session-directory.ts +8 -6
- package/src/session-query.ts +239 -0
- package/src/startup.ts +3 -3
- package/src/subagents.ts +229 -229
- package/src/terminal-title.ts +190 -0
- package/src/theme-panel.ts +17 -21
- package/src/theme.ts +281 -33
- package/src/update-panel.ts +256 -0
- package/src/update.ts +126 -0
- package/src/version.ts +58 -20
- package/src/whale-glyph.ts +23 -23
package/src/models.ts
CHANGED
|
@@ -1,232 +1,284 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model directory for the `/model` panel: the in-process equivalent of the
|
|
3
|
-
* web host's model catalog (`buildModelCatalog`), reading the advisory
|
|
4
|
-
* `ctx.llm` registry directly. Catalog membership is advisory — a route
|
|
5
|
-
* serving a model it stopped advertising stays usable — so selection never
|
|
6
|
-
* fails on catalog absence alone.
|
|
7
|
-
*
|
|
8
|
-
* @module @deepseek-ai/dsh-tui/models
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Context } from '@deepseek-ai/cordis'
|
|
12
|
-
import type { ModelSelection } from '@deepseek-ai/dsh-agent'
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Model directory for the `/model` panel: the in-process equivalent of the
|
|
3
|
+
* web host's model catalog (`buildModelCatalog`), reading the advisory
|
|
4
|
+
* `ctx.llm` registry directly. Catalog membership is advisory — a route
|
|
5
|
+
* serving a model it stopped advertising stays usable — so selection never
|
|
6
|
+
* fails on catalog absence alone.
|
|
7
|
+
*
|
|
8
|
+
* @module @deepseek-ai/dsh-tui/models
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
12
|
+
import type { ModelSelection } from '@deepseek-ai/dsh-agent'
|
|
13
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
|
14
|
+
import {
|
|
15
|
+
ReasoningEffortId,
|
|
16
|
+
type LlmCallConfig,
|
|
16
17
|
type LlmModelInfo,
|
|
17
18
|
type LlmModelReasoningInfo,
|
|
18
19
|
type LlmResolvedModelInfo,
|
|
19
20
|
type ModelModality,
|
|
20
21
|
} from '@deepseek-ai/dsh-llm'
|
|
21
|
-
|
|
22
|
-
/** Display metadata for one adapter-owned reasoning effort (mirrors `LlmReasoningEffortInfo`). */
|
|
23
|
-
export interface ModelReasoningEffort {
|
|
24
|
-
/** Opaque value accepted by the model's `GenerateOptions.reasoningEffort`. */
|
|
25
|
-
id: string
|
|
26
|
-
/** Human-readable effort name for selectors. */
|
|
27
|
-
name: string
|
|
28
|
-
/** Optional user-facing distinction from otherwise similar efforts. */
|
|
29
|
-
description?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Selectable reasoning efforts for one model (mirrors `LlmModelReasoningInfo`). */
|
|
33
|
-
export interface ModelReasoning {
|
|
34
|
-
/** Supported efforts in adapter-preferred display order. */
|
|
35
|
-
efforts: readonly ModelReasoningEffort[]
|
|
36
|
-
/** Adapter-configured default materialized when callers omit an effort. */
|
|
37
|
-
defaultEffort?: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** One selectable row in the `/model` panel. */
|
|
41
|
-
export interface ModelRow {
|
|
42
|
-
/** Registered provider route. */
|
|
43
|
-
provider: string
|
|
44
|
-
/** Display name of the provider route. */
|
|
45
|
-
providerName: string
|
|
46
|
-
/** Provider-owned model id. */
|
|
47
|
-
model: string
|
|
22
|
+
|
|
23
|
+
/** Display metadata for one adapter-owned reasoning effort (mirrors `LlmReasoningEffortInfo`). */
|
|
24
|
+
export interface ModelReasoningEffort {
|
|
25
|
+
/** Opaque value accepted by the model's `GenerateOptions.reasoningEffort`. */
|
|
26
|
+
id: string
|
|
27
|
+
/** Human-readable effort name for selectors. */
|
|
28
|
+
name: string
|
|
29
|
+
/** Optional user-facing distinction from otherwise similar efforts. */
|
|
30
|
+
description?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Selectable reasoning efforts for one model (mirrors `LlmModelReasoningInfo`). */
|
|
34
|
+
export interface ModelReasoning {
|
|
35
|
+
/** Supported efforts in adapter-preferred display order. */
|
|
36
|
+
efforts: readonly ModelReasoningEffort[]
|
|
37
|
+
/** Adapter-configured default materialized when callers omit an effort. */
|
|
38
|
+
defaultEffort?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** One selectable row in the `/model` panel. */
|
|
42
|
+
export interface ModelRow {
|
|
43
|
+
/** Registered provider route. */
|
|
44
|
+
provider: string
|
|
45
|
+
/** Display name of the provider route. */
|
|
46
|
+
providerName: string
|
|
47
|
+
/** Provider-owned model id. */
|
|
48
|
+
model: string
|
|
48
49
|
/** Human-readable model name. */
|
|
49
50
|
modelName: string
|
|
50
51
|
/** Request modalities advertised for this exact route; absent means unknown. */
|
|
51
52
|
inputModalities?: readonly ModelModality[]
|
|
52
53
|
/** Adapter-owned selectable reasoning levels when the model exposes any. */
|
|
53
|
-
reasoning?: ModelReasoning
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** The resolved directory: rows plus per-provider discovery failures. */
|
|
57
|
-
export interface ModelDirectory {
|
|
58
|
-
/** Advisory rows, provider-major in registry order. */
|
|
59
|
-
rows: readonly ModelRow[]
|
|
60
|
-
/** Provider ids whose model listing failed; those providers contribute no rows. */
|
|
61
|
-
failures: readonly string[]
|
|
62
|
-
/**
|
|
63
|
-
* `provider/model` labels whose per-model capability lookup failed. Those
|
|
64
|
-
* rows still appear (advisory degrade, mirroring the web catalog), but
|
|
65
|
-
* without an effort picker — a picker caller must not misread the absence
|
|
66
|
-
* as "this model exposes no reasoning" (e.g. deepseek-v4-flash always
|
|
67
|
-
* advertises off/high/max unless thinking is disabled). Optional for
|
|
68
|
-
* callers that shape a directory by hand; {@link loadModelDirectory}
|
|
69
|
-
* always populates it (empty when nothing failed).
|
|
70
|
-
*/
|
|
71
|
-
reasoningFailures?: readonly string[]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Map an adapter's reasoning capability onto the panel's plain-id shape. */
|
|
75
|
-
export function mapReasoning(reasoning: LlmModelReasoningInfo): ModelReasoning {
|
|
76
|
-
return {
|
|
77
|
-
efforts: reasoning.efforts.map(effort => ({
|
|
78
|
-
id: effort.id,
|
|
79
|
-
name: effort.name,
|
|
80
|
-
...effort.description === undefined ? {} : { description: effort.description },
|
|
81
|
-
})),
|
|
82
|
-
...reasoning.defaultEffort === undefined ? {} : { defaultEffort: reasoning.defaultEffort },
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Resolve the effective model selection for one live session, in the
|
|
88
|
-
* documented precedence: the in-process explicit pick, then the session's
|
|
89
|
-
* last `request/header` config, then the deployment default.
|
|
90
|
-
* @param picked - the explicit selection made in this process, when any.
|
|
91
|
-
* @param logged - the last logged request header config, when any.
|
|
92
|
-
* @param defaults - the deployment default selection.
|
|
93
|
-
* @returns the effective selection, carrying a reasoning effort when one is in force.
|
|
94
|
-
*/
|
|
95
|
-
export function resolveEffectiveSelection(
|
|
96
|
-
picked: ModelSelection | undefined,
|
|
97
|
-
logged: { provider: string; model: string; reasoningEffort?: string } | undefined,
|
|
98
|
-
defaults: ModelSelection,
|
|
99
|
-
): ModelSelection {
|
|
100
|
-
if (picked !== undefined) return picked
|
|
101
|
-
if (logged !== undefined) {
|
|
102
|
-
return {
|
|
103
|
-
provider: logged.provider,
|
|
104
|
-
model: logged.model,
|
|
105
|
-
...logged.reasoningEffort === undefined
|
|
106
|
-
? {}
|
|
107
|
-
: { reasoningEffort: ReasoningEffortId(logged.reasoningEffort) },
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return defaults
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
54
|
+
reasoning?: ModelReasoning
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The resolved directory: rows plus per-provider discovery failures. */
|
|
58
|
+
export interface ModelDirectory {
|
|
59
|
+
/** Advisory rows, provider-major in registry order. */
|
|
60
|
+
rows: readonly ModelRow[]
|
|
61
|
+
/** Provider ids whose model listing failed; those providers contribute no rows. */
|
|
62
|
+
failures: readonly string[]
|
|
63
|
+
/**
|
|
64
|
+
* `provider/model` labels whose per-model capability lookup failed. Those
|
|
65
|
+
* rows still appear (advisory degrade, mirroring the web catalog), but
|
|
66
|
+
* without an effort picker — a picker caller must not misread the absence
|
|
67
|
+
* as "this model exposes no reasoning" (e.g. deepseek-v4-flash always
|
|
68
|
+
* advertises off/high/max unless thinking is disabled). Optional for
|
|
69
|
+
* callers that shape a directory by hand; {@link loadModelDirectory}
|
|
70
|
+
* always populates it (empty when nothing failed).
|
|
71
|
+
*/
|
|
72
|
+
reasoningFailures?: readonly string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Map an adapter's reasoning capability onto the panel's plain-id shape. */
|
|
76
|
+
export function mapReasoning(reasoning: LlmModelReasoningInfo): ModelReasoning {
|
|
77
|
+
return {
|
|
78
|
+
efforts: reasoning.efforts.map(effort => ({
|
|
79
|
+
id: effort.id,
|
|
80
|
+
name: effort.name,
|
|
81
|
+
...effort.description === undefined ? {} : { description: effort.description },
|
|
82
|
+
})),
|
|
83
|
+
...reasoning.defaultEffort === undefined ? {} : { defaultEffort: reasoning.defaultEffort },
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the effective model selection for one live session, in the
|
|
89
|
+
* documented precedence: the in-process explicit pick, then the session's
|
|
90
|
+
* last `request/header` config, then the deployment default.
|
|
91
|
+
* @param picked - the explicit selection made in this process, when any.
|
|
92
|
+
* @param logged - the last logged request header config, when any.
|
|
93
|
+
* @param defaults - the deployment default selection.
|
|
94
|
+
* @returns the effective selection, carrying a reasoning effort when one is in force.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveEffectiveSelection(
|
|
97
|
+
picked: ModelSelection | undefined,
|
|
98
|
+
logged: { provider: string; model: string; reasoningEffort?: string } | undefined,
|
|
99
|
+
defaults: ModelSelection,
|
|
100
|
+
): ModelSelection {
|
|
101
|
+
if (picked !== undefined) return picked
|
|
102
|
+
if (logged !== undefined) {
|
|
103
|
+
return {
|
|
104
|
+
provider: logged.provider,
|
|
105
|
+
model: logged.model,
|
|
106
|
+
...logged.reasoningEffort === undefined
|
|
107
|
+
? {}
|
|
108
|
+
: { reasoningEffort: ReasoningEffortId(logged.reasoningEffort) },
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return defaults
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Structural equality of two selections (upstream `sameSelection`). */
|
|
115
|
+
function sameModelSelection(left: ModelSelection, right: ModelSelection): boolean {
|
|
116
|
+
return left.provider === right.provider
|
|
117
|
+
&& left.model === right.model
|
|
118
|
+
&& left.reasoningEffort === right.reasoningEffort
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The session log's unconsumed explicit model selection (the resume layer of
|
|
123
|
+
* the documented precedence). `model/selection` events are log-only facts the
|
|
124
|
+
* web host's session-controller appends when its user picks a model for the
|
|
125
|
+
* NEXT request; a request header assembling exactly that selection retires
|
|
126
|
+
* it, while a header for different values (another route forced the request)
|
|
127
|
+
* leaves it armed. Seeding the resumed pick from here restores a selection
|
|
128
|
+
* that was made but never sent — the request header alone would answer with
|
|
129
|
+
* the older model.
|
|
130
|
+
* @param events - the full persisted event list of the resumed session.
|
|
131
|
+
* @returns the still-pending selection, when one exists.
|
|
132
|
+
*/
|
|
133
|
+
export function pendingModelSelection(events: readonly SessionEvent[]): ModelSelection | undefined {
|
|
134
|
+
let pending: ModelSelection | undefined
|
|
135
|
+
for (const event of events) {
|
|
136
|
+
// The event map merge lives in @deepseek-ai/dsh-api-session-controller,
|
|
137
|
+
// which this bundle does not depend on; the string guard keeps the fold
|
|
138
|
+
// decoupled while matching the typed upstream projection exactly.
|
|
139
|
+
if ((event.type as string) === 'model/selection') {
|
|
140
|
+
const data = event.data as Partial<ModelSelection>
|
|
141
|
+
if (typeof data.provider === 'string' && typeof data.model === 'string') {
|
|
142
|
+
const selection: ModelSelection = {
|
|
143
|
+
provider: data.provider,
|
|
144
|
+
model: data.model,
|
|
145
|
+
...data.reasoningEffort === undefined ? {} : { reasoningEffort: data.reasoningEffort },
|
|
146
|
+
}
|
|
147
|
+
if (pending === undefined || !sameModelSelection(pending, selection)) pending = selection
|
|
148
|
+
}
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
if (event.type === 'request/header') {
|
|
152
|
+
const config = event.data.header.config
|
|
153
|
+
if (pending !== undefined && sameModelSelection(pending, {
|
|
154
|
+
provider: config.provider,
|
|
155
|
+
model: config.model,
|
|
156
|
+
...config.reasoningEffort === undefined ? {} : { reasoningEffort: config.reasoningEffort },
|
|
157
|
+
})) {
|
|
158
|
+
pending = undefined
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return pending
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Build the selection one `/model` pick applies, rejecting an effort the
|
|
167
|
+
* row does not advertise. The row is the picker's source of truth, so a
|
|
168
|
+
* stale directory cannot smuggle an unsupported effort into the next step
|
|
169
|
+
* (the request pipeline would reject it before network I/O regardless). The
|
|
170
|
+
* empty string is the picker's "provider default" sentinel — an explicit
|
|
171
|
+
* choice to leave the effort to the model's own default, exactly like an
|
|
172
|
+
* absent effort.
|
|
173
|
+
* @param row - the picked model row.
|
|
174
|
+
* @param effortId - the chosen advertised effort, '' or undefined for the model default.
|
|
175
|
+
* @returns the selection the runner records for the next assembled step.
|
|
176
|
+
*/
|
|
177
|
+
export function buildModelSelection(row: ModelRow, effortId?: string): ModelSelection {
|
|
178
|
+
const selected = effortId === undefined || effortId === '' ? undefined : effortId
|
|
179
|
+
if (selected !== undefined
|
|
180
|
+
&& (row.reasoning === undefined || !row.reasoning.efforts.some(effort => effort.id === selected))) {
|
|
181
|
+
throw new Error(`model ${row.provider}/${row.model} does not support reasoning effort "${selected}"`)
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
provider: row.provider,
|
|
185
|
+
model: row.model,
|
|
186
|
+
...selected === undefined ? {} : { reasoningEffort: ReasoningEffortId(selected) },
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Display label for one applied selection: `provider/model` or `provider/model@effort`. */
|
|
191
|
+
export function modelSelectionLabel(selection: ModelSelection): string {
|
|
192
|
+
return selection.reasoningEffort === undefined
|
|
193
|
+
? `${selection.provider}/${selection.model}`
|
|
194
|
+
: `${selection.provider}/${selection.model}@${selection.reasoningEffort}`
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Apply one model selection onto a resolved request config — the exact
|
|
199
|
+
* semantics of the kernel's `installModelSelection` request listener,
|
|
200
|
+
* extracted so the TUI can mirror it for subagent-origin requests: children
|
|
201
|
+
* spawned by the subagent tool inherit the parent's CREATE-TIME AgentOptions,
|
|
202
|
+
* which a mid-session /model switch never touches, so delegated work would
|
|
203
|
+
* otherwise keep running on the launch-time route. An absent effort strips
|
|
204
|
+
* any inherited effort (restoring the selected model's provider default),
|
|
205
|
+
* matching the kernel listener field-for-field.
|
|
206
|
+
* @param resolved - the config the inner chain produced.
|
|
207
|
+
* @param selection - the selection to enforce.
|
|
208
|
+
* @returns the overridden config.
|
|
209
|
+
*/
|
|
210
|
+
export function applyModelSelectionToConfig(resolved: LlmCallConfig, selection: ModelSelection): LlmCallConfig {
|
|
211
|
+
const { reasoningEffort: _inheritedEffort, ...withoutInheritedEffort } = resolved
|
|
212
|
+
return {
|
|
213
|
+
...withoutInheritedEffort,
|
|
214
|
+
provider: selection.provider,
|
|
215
|
+
model: selection.model,
|
|
216
|
+
...selection.reasoningEffort === undefined
|
|
217
|
+
? {}
|
|
218
|
+
: { reasoningEffort: selection.reasoningEffort },
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Load the selectable model directory from the live `ctx.llm` registry.
|
|
224
|
+
* Providers are listed synchronously; each provider's models are discovered
|
|
225
|
+
* with a bounded parallel fan-out whose failures degrade to that provider
|
|
226
|
+
* contributing no rows (mirrors the web catalog's per-provider failures).
|
|
227
|
+
* Each row's reasoning levels are resolved per exact model like the web
|
|
228
|
+
* catalog (`buildModelCatalog`); a single model's capability lookup failure
|
|
229
|
+
* degrades to that row having no effort picker rather than hiding the model,
|
|
230
|
+
* and the failure rides `reasoningFailures` so the caller can tell "no
|
|
231
|
+
* advertised reasoning" from "capability lookup failed".
|
|
232
|
+
* @param ctx - context carrying the `llm` service.
|
|
233
|
+
* @returns the resolved directory; empty rows when `llm` is unavailable.
|
|
234
|
+
*/
|
|
235
|
+
export async function loadModelDirectory(ctx: Context): Promise<ModelDirectory> {
|
|
236
|
+
const llm = ctx.get('llm')
|
|
237
|
+
if (llm === undefined) return { rows: [], failures: [], reasoningFailures: [] }
|
|
238
|
+
// Call resolveModelInfo AS A METHOD (llm.resolveModelInfo(...)): destructured
|
|
239
|
+
// off the service it loses `this` — this.registration() then throws on the
|
|
240
|
+
// first provider, the catch swallows it, and every row lands in
|
|
241
|
+
// reasoningFailures ("capability lookup failed") even though the adapter
|
|
242
|
+
// itself never fails.
|
|
243
|
+
const llmResolve = llm as { resolveModelInfo?: (provider: string, model: string) => Promise<LlmResolvedModelInfo> }
|
|
244
|
+
const providers = llm.listProviders()
|
|
245
|
+
const listed = await Promise.all(providers.map(async (provider) => {
|
|
246
|
+
try {
|
|
247
|
+
const models: readonly LlmModelInfo[] = await llm.listModels(provider.id)
|
|
248
|
+
const reasoningFailures: string[] = []
|
|
249
|
+
const rows = await Promise.all(models.map(async (model): Promise<ModelRow> => {
|
|
250
|
+
const row: ModelRow = {
|
|
251
|
+
provider: provider.id,
|
|
200
252
|
providerName: provider.name,
|
|
201
253
|
model: model.id,
|
|
202
254
|
modelName: model.name,
|
|
203
255
|
...model.inputModalities === undefined ? {} : { inputModalities: [...model.inputModalities] },
|
|
204
256
|
}
|
|
205
|
-
if (llmResolve.resolveModelInfo === undefined) return row
|
|
206
|
-
try {
|
|
207
|
-
const resolved = await llmResolve.resolveModelInfo(provider.id, model.id)
|
|
257
|
+
if (llmResolve.resolveModelInfo === undefined) return row
|
|
258
|
+
try {
|
|
259
|
+
const resolved = await llmResolve.resolveModelInfo(provider.id, model.id)
|
|
208
260
|
return {
|
|
209
261
|
...row,
|
|
210
262
|
...resolved.inputModalities === undefined ? {} : { inputModalities: [...resolved.inputModalities] },
|
|
211
263
|
...resolved.reasoning === undefined ? {} : { reasoning: mapReasoning(resolved.reasoning) },
|
|
212
264
|
}
|
|
213
|
-
} catch {
|
|
214
|
-
reasoningFailures.push(`${provider.id}/${model.id}`)
|
|
215
|
-
return row
|
|
216
|
-
}
|
|
217
|
-
}))
|
|
218
|
-
return {
|
|
219
|
-
provider: provider.id,
|
|
220
|
-
providerName: provider.name,
|
|
221
|
-
models: rows,
|
|
222
|
-
reasoningFailures,
|
|
223
|
-
}
|
|
224
|
-
} catch {
|
|
225
|
-
return { provider: provider.id, providerName: provider.name, models: [] as ModelRow[], failed: true }
|
|
226
|
-
}
|
|
227
|
-
}))
|
|
228
|
-
const rows = listed.flatMap(entry => entry.models)
|
|
229
|
-
const failures = listed.filter(entry => 'failed' in entry && entry.failed === true).map(entry => entry.provider)
|
|
230
|
-
const reasoningFailures = listed.flatMap(entry => 'failed' in entry ? [] : entry.reasoningFailures)
|
|
231
|
-
return { rows, failures, reasoningFailures }
|
|
232
|
-
}
|
|
265
|
+
} catch {
|
|
266
|
+
reasoningFailures.push(`${provider.id}/${model.id}`)
|
|
267
|
+
return row
|
|
268
|
+
}
|
|
269
|
+
}))
|
|
270
|
+
return {
|
|
271
|
+
provider: provider.id,
|
|
272
|
+
providerName: provider.name,
|
|
273
|
+
models: rows,
|
|
274
|
+
reasoningFailures,
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
return { provider: provider.id, providerName: provider.name, models: [] as ModelRow[], failed: true }
|
|
278
|
+
}
|
|
279
|
+
}))
|
|
280
|
+
const rows = listed.flatMap(entry => entry.models)
|
|
281
|
+
const failures = listed.filter(entry => 'failed' in entry && entry.failed === true).map(entry => entry.provider)
|
|
282
|
+
const reasoningFailures = listed.flatMap(entry => 'failed' in entry ? [] : entry.reasoningFailures)
|
|
283
|
+
return { rows, failures, reasoningFailures }
|
|
284
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable per-panel accents for the prismatic theme: every bordered panel
|
|
3
|
+
* claims the next slot in a first-seen sequence keyed by its stable id, so a
|
|
4
|
+
* screen with several open panels shows several ring colors while each panel
|
|
5
|
+
* keeps one learnable color. Deliberately NOT a React hook — several panels
|
|
6
|
+
* render through direct function calls inside conditionals, where hooks are
|
|
7
|
+
* illegal — and identity is by panel id, not by mount. Outside prismatic the
|
|
8
|
+
* helper is an identity: callers pass their usual border/title colors
|
|
9
|
+
* through unchanged and dark/light render pixel-identically.
|
|
10
|
+
*
|
|
11
|
+
* @module @deepseek-ai/dsh-tui/panel-accent
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { surfaceAccent, type RgbTriple } from './theme.ts'
|
|
15
|
+
|
|
16
|
+
/** First-seen slot per panel id; stable for the process lifetime. */
|
|
17
|
+
const panelSlots = new Map<string, number>()
|
|
18
|
+
|
|
19
|
+
/** One panel's resolved accent pair (border and title share the ring color in prismatic). */
|
|
20
|
+
export interface PanelAccent {
|
|
21
|
+
readonly border: RgbTriple
|
|
22
|
+
readonly title: RgbTriple
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The accent pair for one panel: in prismatic both entries are the panel's
|
|
27
|
+
* ring color (border and title match, per the design); in every other theme
|
|
28
|
+
* they are exactly the colors passed in.
|
|
29
|
+
* @param id - stable panel identity ('todos', 'model', 'kernel-list', …).
|
|
30
|
+
* @param border - the border color used outside prismatic.
|
|
31
|
+
* @param title - the title color used outside prismatic (defaults to border).
|
|
32
|
+
* @returns the resolved accent pair for this panel.
|
|
33
|
+
*/
|
|
34
|
+
export function panelAccent(id: string, border: RgbTriple, title: RgbTriple = border): PanelAccent {
|
|
35
|
+
let slot = panelSlots.get(id)
|
|
36
|
+
if (slot === undefined) {
|
|
37
|
+
slot = panelSlots.size
|
|
38
|
+
panelSlots.set(id, slot)
|
|
39
|
+
}
|
|
40
|
+
return { border: surfaceAccent(slot, border), title: surfaceAccent(slot, title) }
|
|
41
|
+
}
|
package/src/presets.ts
CHANGED
|
@@ -38,7 +38,7 @@ export function resolvePreset(session: Pick<Session, 'header' | 'snapshotEvents'
|
|
|
38
38
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
39
39
|
const event = events[index] as unknown as { type: string; data?: { agentPreset?: string } }
|
|
40
40
|
if (event.type === 'agent-preset/selected' && event.data?.agentPreset !== undefined) {
|
|
41
|
-
return normalizePresetId(event.data.agentPreset)
|
|
41
|
+
return normalizePresetId(event.data.agentPreset)
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
return normalizePresetId(session.header.agentPreset) ?? 'standard'
|
package/src/provider-settings.ts
CHANGED
|
@@ -722,7 +722,7 @@ export async function discoverProviderModels(
|
|
|
722
722
|
const draft = hasUrl
|
|
723
723
|
? {
|
|
724
724
|
...(oneShotKey !== undefined && oneShotKey !== '' ? { apiKey: oneShotKey } : {}),
|
|
725
|
-
baseURL: baseURL
|
|
725
|
+
baseURL: baseURL,
|
|
726
726
|
...target.configuration.api === undefined ? {} : { api: target.configuration.api },
|
|
727
727
|
}
|
|
728
728
|
: {
|