deepseek-cli-mode 1.3.0 → 1.5.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/CHANGELOG.md +23 -0
- package/README.md +5 -4
- package/package.json +1 -1
- package/runner/line-editor.js +13 -3
- package/runner/runner.js +203 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0 — `/api` configuration and coloured permission footer
|
|
4
|
+
|
|
5
|
+
- **`/api`** configures the DeepSeek endpoint from inside the CLI:
|
|
6
|
+
- `/api` opens a menu (Show status / Set API key / Set base URL / Remove key)
|
|
7
|
+
- `/api set <key>` or bare `/api <key>` stores the key through the Harness credential service, which writes
|
|
8
|
+
`$DSH_HOME/.credentials.yaml` immediately — no restart, the next request uses it
|
|
9
|
+
- `/api url <https://…>` writes `baseURL` into `$DSH_HOME/settings.yaml`
|
|
10
|
+
- `/api unset` removes the stored key; `/api status` prints key state, base URL, and both file paths
|
|
11
|
+
- The key prompt is masked (`*`) in the interactive editor
|
|
12
|
+
- The permission footer is colour-coded: **`⏵ danger-full-access` in red**, `read-only` in green,
|
|
13
|
+
`workspace-write` in yellow; the rest of the line stays dim.
|
|
14
|
+
- Security: keys are never printed back; the value is passed only to the credential service. `/api` is a user-typed
|
|
15
|
+
command — the model cannot change credentials.
|
|
16
|
+
|
|
17
|
+
## 1.4.0 — reasoning effort levels in the model picker
|
|
18
|
+
|
|
19
|
+
- `/model` now asks for the reasoning effort after you pick a model, listing the adapter's real levels
|
|
20
|
+
(`low`, `medium`, `high`, `max`) plus `default`, with the current value preselected.
|
|
21
|
+
- `/reasoning` lists the same adapter-provided levels (falling back to `low`/`medium`/`high`/`max` when the
|
|
22
|
+
adapter exposes no metadata).
|
|
23
|
+
- The footer and banner show the active model **and** effort, e.g. `deepseek-flash · high`.
|
|
24
|
+
- Direct arguments still work: `/reasoning low`, `/model deepseek-v4-pro`.
|
|
25
|
+
|
|
3
26
|
## 1.3.0 — pickers, Shift+Tab permission cycling, permanent mode footer
|
|
4
27
|
|
|
5
28
|
- `/model`, `/reasoning`, `/permissions`, and `/resume` now open an interactive picker (`↑`/`↓`, `Enter`,
|
package/README.md
CHANGED
|
@@ -145,10 +145,10 @@ Typing `/` opens a live command menu, like Claude Code:
|
|
|
145
145
|
`↑`/`↓` moves the selection, `Tab` completes it, `Enter` runs it. The menu filters as you type.
|
|
146
146
|
|
|
147
147
|
`/model`, `/reasoning`, `/permissions`, and `/resume` open the same kind of picker when called without an argument.
|
|
148
|
-
**Shift+Tab** cycles the permission mode, and a
|
|
148
|
+
**Shift+Tab** cycles the permission mode, and a footer above the prompt always shows the active one — `danger-full-access` in red, `read-only` in green, `workspace-write` in yellow:
|
|
149
149
|
|
|
150
150
|
```
|
|
151
|
-
|
|
151
|
+
⏵ workspace-write · deepseek-official/deepseek-chat · shift+tab to cycle
|
|
152
152
|
>
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -158,8 +158,9 @@ Typing `/` opens a live command menu, like Claude Code:
|
|
|
158
158
|
| `/new` | start a fresh session (new id, keeps the process warm) |
|
|
159
159
|
| `/sessions` | recent sessions in this directory (with a ▶ running marker) |
|
|
160
160
|
| `/resume <id>` | switch to another session |
|
|
161
|
-
| `/model [id]` |
|
|
162
|
-
| `/reasoning [
|
|
161
|
+
| `/model [id]` | pick a model and its reasoning effort |
|
|
162
|
+
| `/reasoning [effort]` | pick a reasoning effort (low/medium/high/max) |
|
|
163
|
+
| `/api [set <key>\|url <url>\|unset\|status]` | configure the API key and endpoint; writes `$DSH_HOME/.credentials.yaml` and `settings.yaml` |
|
|
163
164
|
| `/permission [preset]` | switch the sandbox + approval preset |
|
|
164
165
|
| `/skills` | list the skills discovered in this workspace |
|
|
165
166
|
| `/jobs` | list this session's background jobs |
|
package/package.json
CHANGED
package/runner/line-editor.js
CHANGED
|
@@ -55,6 +55,7 @@ export class LineEditor {
|
|
|
55
55
|
this.selectOptions = [];
|
|
56
56
|
this.selectIndex = 0;
|
|
57
57
|
this.hint = "";
|
|
58
|
+
this.secret = false;
|
|
58
59
|
this._onData = (chunk) => this.handleData(chunk);
|
|
59
60
|
this._onEnd = () => {
|
|
60
61
|
this.onEnd();
|
|
@@ -99,11 +100,13 @@ export class LineEditor {
|
|
|
99
100
|
* Ask one line.
|
|
100
101
|
* @param {string} prompt prompt string (may contain ANSI)
|
|
101
102
|
* @param {boolean} menuEnabled show slash-command suggestions
|
|
103
|
+
* @param {boolean} [secret] mask the typed value (API keys, tokens)
|
|
102
104
|
* @returns {Promise<string|null>} line, or null on Ctrl+C / EOF
|
|
103
105
|
*/
|
|
104
|
-
ask(prompt, menuEnabled = false) {
|
|
106
|
+
ask(prompt, menuEnabled = false, secret = false) {
|
|
105
107
|
this.prompt = prompt;
|
|
106
108
|
this.menuEnabled = menuEnabled;
|
|
109
|
+
this.secret = secret === true;
|
|
107
110
|
this.buffer = "";
|
|
108
111
|
this.cursor = 0;
|
|
109
112
|
this.menu = [];
|
|
@@ -118,6 +121,11 @@ export class LineEditor {
|
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
|
|
124
|
+
/** Ask for a secret value without echoing it. */
|
|
125
|
+
askSecret(prompt) {
|
|
126
|
+
return this.ask(prompt, false, true);
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
/**
|
|
122
130
|
* Ask the user to pick one option from a list.
|
|
123
131
|
* @param {string} title prompt line
|
|
@@ -371,6 +379,7 @@ export class LineEditor {
|
|
|
371
379
|
this.selectMode = false;
|
|
372
380
|
this.selectOptions = [];
|
|
373
381
|
this.hint = "";
|
|
382
|
+
this.secret = false;
|
|
374
383
|
this.clearRender();
|
|
375
384
|
this.menu = [];
|
|
376
385
|
this.output.write("\x1b[?25h");
|
|
@@ -409,9 +418,10 @@ export class LineEditor {
|
|
|
409
418
|
const prompt = this.prompt;
|
|
410
419
|
const promptWidth = visibleWidth(prompt);
|
|
411
420
|
const available = Math.max(8, columns - promptWidth - 1);
|
|
421
|
+
const display = this.secret ? "*".repeat(this.buffer.length) : this.buffer;
|
|
412
422
|
let offset = 0;
|
|
413
|
-
if (
|
|
414
|
-
const visible =
|
|
423
|
+
if (display.length > available) offset = Math.max(0, this.cursor - available + 1);
|
|
424
|
+
const visible = display.slice(offset, offset + available);
|
|
415
425
|
const column = promptWidth + (this.cursor - offset);
|
|
416
426
|
this.output.write(prompt + visible);
|
|
417
427
|
|
package/runner/runner.js
CHANGED
|
@@ -23,6 +23,7 @@ import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
|
23
23
|
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
24
24
|
import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
25
25
|
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
26
|
+
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
26
27
|
import { LineEditor } from "./line-editor.js";
|
|
27
28
|
|
|
28
29
|
/** Stable Cordis plugin name. */
|
|
@@ -497,6 +498,7 @@ function printBanner(model) {
|
|
|
497
498
|
// Slash-command suggestions for the live menu (native Harness commands are
|
|
498
499
|
// merged in at runtime).
|
|
499
500
|
const BUILTIN_COMMANDS = [
|
|
501
|
+
{ name: "api", description: "Set the API key or base URL" },
|
|
500
502
|
{ name: "clear", description: "Clear the screen" },
|
|
501
503
|
{ name: "exit", description: "Leave the session" },
|
|
502
504
|
{ name: "export", description: "Write this run's transcript as markdown" },
|
|
@@ -547,8 +549,9 @@ Interactive commands:
|
|
|
547
549
|
/new start a fresh session
|
|
548
550
|
/sessions list recent sessions in this directory
|
|
549
551
|
/resume <id> switch to another session
|
|
550
|
-
/model [id] pick a model
|
|
552
|
+
/model [id] pick a model and its reasoning effort (low/medium/high/max)
|
|
551
553
|
/reasoning [off|high|max] pick a reasoning effort
|
|
554
|
+
/api [set <key>|url <url>|unset|status] configure the API key and endpoint
|
|
552
555
|
/permissions [p] pick a permission mode (native: /permission; Shift+Tab cycles)
|
|
553
556
|
/skills list the skills discovered in this workspace
|
|
554
557
|
/jobs list this session's background jobs
|
|
@@ -644,6 +647,8 @@ async function run(ctx, options, exit) {
|
|
|
644
647
|
const attachments = ctx.get("attachments");
|
|
645
648
|
const tokenMeter = ctx.get("tokenMeter");
|
|
646
649
|
const llm = ctx.get("llm");
|
|
650
|
+
const credentials = ctx.get("credentials");
|
|
651
|
+
const settings = ctx.get("settings");
|
|
647
652
|
|
|
648
653
|
if (options.list) {
|
|
649
654
|
printSessionList(listSessions());
|
|
@@ -1029,10 +1034,23 @@ async function run(ctx, options, exit) {
|
|
|
1029
1034
|
}
|
|
1030
1035
|
}
|
|
1031
1036
|
|
|
1037
|
+
/** Colour the active permission mode by risk. */
|
|
1038
|
+
function modeColor(mode) {
|
|
1039
|
+
if (mode.includes("danger")) return ansi.red;
|
|
1040
|
+
if (mode.includes("read")) return ansi.green;
|
|
1041
|
+
return ansi.yellow;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1032
1044
|
/** Permanent one-line footer above the prompt. */
|
|
1033
1045
|
function printStatusBar() {
|
|
1034
1046
|
if (!tty || !interactive) return;
|
|
1035
|
-
|
|
1047
|
+
const mode = currentPermission();
|
|
1048
|
+
out(
|
|
1049
|
+
" " +
|
|
1050
|
+
paint(modeColor(mode), `⏵ ${sanitize(mode)}`) +
|
|
1051
|
+
paint(ansi.dim, ` · ${sanitize(modelDesc())} · shift+tab to cycle`) +
|
|
1052
|
+
"\n"
|
|
1053
|
+
);
|
|
1036
1054
|
}
|
|
1037
1055
|
|
|
1038
1056
|
/** One list picker shared by /model, /reasoning, /permissions, /resume. */
|
|
@@ -1051,7 +1069,95 @@ async function run(ctx, options, exit) {
|
|
|
1051
1069
|
return options[index]?.value ?? null;
|
|
1052
1070
|
}
|
|
1053
1071
|
|
|
1054
|
-
|
|
1072
|
+
// ---- API key / endpoint management --------------------------------------
|
|
1073
|
+
const API_KEY_REF = credentialRef("DEEPSEEK_API_KEY");
|
|
1074
|
+
const DEFAULT_BASE_URL = "https://api.deepseek.com";
|
|
1075
|
+
|
|
1076
|
+
async function apiBaseUrl() {
|
|
1077
|
+
try {
|
|
1078
|
+
const descriptor = settings?.describe().find((entry) => String(entry.ns) === "llm-deepseek");
|
|
1079
|
+
const value = descriptor?.value;
|
|
1080
|
+
if (value && typeof value === "object" && typeof value.baseURL === "string" && value.baseURL !== "") return value.baseURL;
|
|
1081
|
+
} catch {}
|
|
1082
|
+
return DEFAULT_BASE_URL;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
async function printApiStatus() {
|
|
1086
|
+
let info = null;
|
|
1087
|
+
try {
|
|
1088
|
+
info = credentials === undefined ? null : await credentials.describe(API_KEY_REF);
|
|
1089
|
+
} catch {}
|
|
1090
|
+
const keyState = info?.configured
|
|
1091
|
+
? `configured (source: ${info.source}${info.writable === false ? ", read-only" : ""})`
|
|
1092
|
+
: "not configured";
|
|
1093
|
+
out(paint(ansi.bold, " API configuration") + "\n");
|
|
1094
|
+
out(` key: ${sanitize(keyState)}\n`);
|
|
1095
|
+
out(` base: ${sanitize(await apiBaseUrl())}\n`);
|
|
1096
|
+
out(paint(ansi.dim, ` credentials file: ${join(resolveDshHome(), ".credentials.yaml")}`) + "\n");
|
|
1097
|
+
out(paint(ansi.dim, ` settings file: ${join(resolveDshHome(), "settings.yaml")}`) + "\n");
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
async function storeApiKey(raw) {
|
|
1101
|
+
const key = String(raw).trim();
|
|
1102
|
+
if (key === "") {
|
|
1103
|
+
out(paint(ansi.yellow, " empty key — nothing stored\n"));
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
if (/\s/.test(key)) {
|
|
1107
|
+
out(paint(ansi.red, " API key must not contain whitespace\n"));
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
if (credentials === undefined) {
|
|
1111
|
+
out(paint(ansi.yellow, " this profile has no credential store\n"));
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
try {
|
|
1115
|
+
await credentials.set(API_KEY_REF, key);
|
|
1116
|
+
out(paint(ansi.green, ` ✓ API key saved to ${sanitize(join(resolveDshHome(), ".credentials.yaml"))}\n`));
|
|
1117
|
+
out(paint(ansi.dim, " new requests use it immediately\n"));
|
|
1118
|
+
return true;
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
out(paint(ansi.red, ` ✘ ${sanitize(describeError(error))}\n`));
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
async function storeBaseUrl(raw) {
|
|
1126
|
+
const url = String(raw).trim();
|
|
1127
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
1128
|
+
out(paint(ansi.red, " base URL must start with http:// or https://\n"));
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
if (settings === undefined) {
|
|
1132
|
+
out(paint(ansi.yellow, " this profile has no settings service\n"));
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
try {
|
|
1136
|
+
await settings.update("llm-deepseek", { baseURL: url });
|
|
1137
|
+
out(paint(ansi.green, ` ✓ base URL saved: ${sanitize(url)}\n`));
|
|
1138
|
+
return true;
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
out(paint(ansi.red, ` ✘ ${sanitize(describeError(error))}\n`));
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function askSecret(prompt) {
|
|
1146
|
+
return editor ? editor.askSecret(prompt) : ask(prompt);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/** Adapter-owned reasoning levels for the selected model, when exposed. */
|
|
1150
|
+
async function reasoningInfo() {
|
|
1151
|
+
try {
|
|
1152
|
+
if (llm === undefined) return null;
|
|
1153
|
+
const resolved = await llm.resolveModel(selectionRef.current.provider, selectionRef.current.model);
|
|
1154
|
+
return resolved.reasoning ?? null;
|
|
1155
|
+
} catch {
|
|
1156
|
+
return null;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
async function applyModelSelection(modelId, pickEffort = true) {
|
|
1055
1161
|
const next = { ...selectionRef.current, model: modelId };
|
|
1056
1162
|
try {
|
|
1057
1163
|
await defaultModel.saveSelection(next);
|
|
@@ -1059,7 +1165,23 @@ async function run(ctx, options, exit) {
|
|
|
1059
1165
|
out(paint(ansi.green, ` ✓ model set: ${sanitize(next.provider)}/${sanitize(next.model)} (next message)\n`));
|
|
1060
1166
|
} catch (error) {
|
|
1061
1167
|
out(paint(ansi.red, ` ✘ ${sanitize(describeError(error))}\n`));
|
|
1168
|
+
return;
|
|
1062
1169
|
}
|
|
1170
|
+
if (!pickEffort) return;
|
|
1171
|
+
const info = await reasoningInfo();
|
|
1172
|
+
const effortOptions =
|
|
1173
|
+
info !== null && info.efforts.length > 0
|
|
1174
|
+
? info.efforts.map((effort) => ({ label: effort.name, description: effort.id, value: effort.id }))
|
|
1175
|
+
: ["low", "medium", "high", "max"].map((effort) => ({ label: effort, description: `effort: ${effort}`, value: effort }));
|
|
1176
|
+
const options = [...effortOptions, { label: "default", description: "provider default", value: "default" }];
|
|
1177
|
+
const defaultEffort = info !== null && info.defaultEffort !== undefined ? info.defaultEffort : "default";
|
|
1178
|
+
// Offer the effort choice for the newly selected model, keeping the current
|
|
1179
|
+
// value when it is still valid.
|
|
1180
|
+
const currentEffort = selectionRef.current.reasoningEffort ?? "default";
|
|
1181
|
+
const preferred = options.some((option) => option.value === currentEffort) ? currentEffort : defaultEffort;
|
|
1182
|
+
const initial = Math.max(0, options.findIndex((option) => option.value === preferred));
|
|
1183
|
+
const chosen = await selectOption(`Reasoning effort for ${modelId}`, options, initial);
|
|
1184
|
+
if (chosen !== null) await applyReasoningSelection(chosen);
|
|
1063
1185
|
}
|
|
1064
1186
|
|
|
1065
1187
|
async function applyReasoningSelection(effort) {
|
|
@@ -1364,6 +1486,69 @@ async function run(ctx, options, exit) {
|
|
|
1364
1486
|
} catch {}
|
|
1365
1487
|
continue;
|
|
1366
1488
|
}
|
|
1489
|
+
case "/api": {
|
|
1490
|
+
const trimmed = argument.trim();
|
|
1491
|
+
if (trimmed === "") {
|
|
1492
|
+
if (!editor) {
|
|
1493
|
+
await printApiStatus();
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
const action = await selectOption(
|
|
1497
|
+
"API configuration",
|
|
1498
|
+
[
|
|
1499
|
+
{ label: "Show status", description: "key, base URL, files", value: "status" },
|
|
1500
|
+
{ label: "Set DeepSeek API key", description: "saved to $DSH_HOME/.credentials.yaml", value: "key" },
|
|
1501
|
+
{ label: "Set base URL", description: "saved to $DSH_HOME/settings.yaml", value: "url" },
|
|
1502
|
+
{ label: "Remove stored API key", description: "falls back to environment", value: "unset" },
|
|
1503
|
+
],
|
|
1504
|
+
0
|
|
1505
|
+
);
|
|
1506
|
+
if (action === "status") await printApiStatus();
|
|
1507
|
+
else if (action === "key") {
|
|
1508
|
+
const key = await askSecret(paint(ansi.green, " API key: "));
|
|
1509
|
+
if (key !== null) await storeApiKey(key);
|
|
1510
|
+
} else if (action === "url") {
|
|
1511
|
+
const url = await ask(paint(ansi.green, " Base URL: "));
|
|
1512
|
+
if (url !== null) await storeBaseUrl(url);
|
|
1513
|
+
} else if (action === "unset") {
|
|
1514
|
+
try {
|
|
1515
|
+
await credentials?.unset(API_KEY_REF);
|
|
1516
|
+
out(paint(ansi.green, " ✓ stored API key removed\n"));
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
out(paint(ansi.red, ` ✘ ${sanitize(describeError(error))}\n`));
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
const [subcommand, ...rest] = trimmed.split(/\s+/);
|
|
1524
|
+
const value = rest.join(" ").trim();
|
|
1525
|
+
if (subcommand === "status" || subcommand === "show") {
|
|
1526
|
+
await printApiStatus();
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (subcommand === "unset" || subcommand === "remove" || subcommand === "clear") {
|
|
1530
|
+
try {
|
|
1531
|
+
await credentials?.unset(API_KEY_REF);
|
|
1532
|
+
out(paint(ansi.green, " ✓ stored API key removed\n"));
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
out(paint(ansi.red, ` ✘ ${sanitize(describeError(error))}\n`));
|
|
1535
|
+
}
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (subcommand === "url" || subcommand === "base" || subcommand === "base-url") {
|
|
1539
|
+
if (value === "") out(paint(ansi.yellow, " usage: /api url <https://endpoint>\n"));
|
|
1540
|
+
else await storeBaseUrl(value);
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
if (subcommand === "set" || subcommand === "key") {
|
|
1544
|
+
if (value === "") out(paint(ansi.yellow, " usage: /api set <key>\n"));
|
|
1545
|
+
else await storeApiKey(value);
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
// Bare value: treat it as the API key.
|
|
1549
|
+
await storeApiKey(trimmed);
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1367
1552
|
case "/model": {
|
|
1368
1553
|
if (argument !== "") {
|
|
1369
1554
|
await applyModelSelection(argument);
|
|
@@ -1392,17 +1577,22 @@ async function run(ctx, options, exit) {
|
|
|
1392
1577
|
await applyReasoningSelection(argument);
|
|
1393
1578
|
continue;
|
|
1394
1579
|
}
|
|
1395
|
-
const
|
|
1580
|
+
const info = await reasoningInfo();
|
|
1581
|
+
const efforts =
|
|
1582
|
+
info !== null && info.efforts.length > 0
|
|
1583
|
+
? info.efforts.map((effort) => ({
|
|
1584
|
+
label: effort.name,
|
|
1585
|
+
description: effort.description ?? effort.id,
|
|
1586
|
+
value: effort.id,
|
|
1587
|
+
}))
|
|
1588
|
+
: ["low", "medium", "high", "max"].map((effort) => ({
|
|
1589
|
+
label: effort,
|
|
1590
|
+
description: `effort: ${effort}`,
|
|
1591
|
+
value: effort,
|
|
1592
|
+
}));
|
|
1593
|
+
efforts.push({ label: "default", description: "provider default", value: "default" });
|
|
1396
1594
|
const current = selectionRef.current.reasoningEffort ?? "default";
|
|
1397
|
-
const chosen = await selectOption(
|
|
1398
|
-
"Select reasoning effort",
|
|
1399
|
-
efforts.map((effort) => ({
|
|
1400
|
-
label: effort,
|
|
1401
|
-
description: effort === "default" ? "provider default" : `effort: ${effort}`,
|
|
1402
|
-
value: effort,
|
|
1403
|
-
})),
|
|
1404
|
-
Math.max(0, efforts.indexOf(current))
|
|
1405
|
-
);
|
|
1595
|
+
const chosen = await selectOption("Select reasoning effort", efforts, Math.max(0, efforts.findIndex((option) => option.value === current)));
|
|
1406
1596
|
if (chosen !== null) await applyReasoningSelection(chosen);
|
|
1407
1597
|
continue;
|
|
1408
1598
|
}
|