laohuang 0.8.1 → 0.8.3
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.md +9 -2
- package/dist/bin.js +189 -68
- package/dist/bin.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,8 +29,15 @@ Google Vertex, OAuth-only providers, and OpenAI Codex. Use `/providers` to see
|
|
|
29
29
|
available, configured, and verified status independently.
|
|
30
30
|
|
|
31
31
|
`/login <provider>` stores API-key credentials, `/logout <provider>` removes
|
|
32
|
-
them, `/model
|
|
33
|
-
|
|
32
|
+
them, and `/model` opens a searchable list of all available models from
|
|
33
|
+
configured providers, including credentials supplied through environment variables.
|
|
34
|
+
Search by provider ID, model ID, or model name; scroll to browse the full list.
|
|
35
|
+
If no models are available, the CLI points to `/login`.
|
|
36
|
+
`/model <provider>` narrows the list, `/model <provider> <model>` switches
|
|
37
|
+
directly, and `/apikey` remains a compatibility alias. Switching models affects
|
|
38
|
+
only the current session, not the default profile.
|
|
39
|
+
|
|
40
|
+
`verified` means an explicitly authorized live native
|
|
34
41
|
tool-call/tool-result E2E was recorded; no providers are checked in as verified
|
|
35
42
|
by default.
|
|
36
43
|
|
package/dist/bin.js
CHANGED
|
@@ -7288,9 +7288,9 @@ var MainScreenRenderer = class _MainScreenRenderer {
|
|
|
7288
7288
|
const width = Math.max(1, size.columns);
|
|
7289
7289
|
const height = Math.max(1, size.rows);
|
|
7290
7290
|
const newLines = frame.lines;
|
|
7291
|
-
_MainScreenRenderer.#validateLines(newLines, width);
|
|
7292
7291
|
const widthChanged = this.#previousWidth !== 0 && this.#previousWidth !== width;
|
|
7293
7292
|
const heightChanged = this.#previousHeight !== 0 && this.#previousHeight !== height;
|
|
7293
|
+
_MainScreenRenderer.#validateLines(newLines, width, widthChanged || heightChanged ? [] : this.#previousLines);
|
|
7294
7294
|
const previousBufferLength = this.#previousHeight > 0 ? this.#previousViewportTop + this.#previousHeight : height;
|
|
7295
7295
|
const prevViewportTop = heightChanged ? Math.max(0, previousBufferLength - height) : this.#previousViewportTop;
|
|
7296
7296
|
let viewportTop = prevViewportTop;
|
|
@@ -7509,8 +7509,10 @@ var MainScreenRenderer = class _MainScreenRenderer {
|
|
|
7509
7509
|
this.#previousWidth = width;
|
|
7510
7510
|
this.#previousHeight = height;
|
|
7511
7511
|
}
|
|
7512
|
-
static #validateLines(lines2, width) {
|
|
7512
|
+
static #validateLines(lines2, width, previous) {
|
|
7513
7513
|
lines2.forEach((line2, index) => {
|
|
7514
|
+
if (previous[index] === line2)
|
|
7515
|
+
return;
|
|
7514
7516
|
if (/[\r\n]/u.test(line2)) {
|
|
7515
7517
|
throw new Error(`rendered line ${index} contains a physical newline`);
|
|
7516
7518
|
}
|
|
@@ -7521,15 +7523,28 @@ var MainScreenRenderer = class _MainScreenRenderer {
|
|
|
7521
7523
|
});
|
|
7522
7524
|
}
|
|
7523
7525
|
};
|
|
7526
|
+
var WIDTH_CACHE_SIZE = 512;
|
|
7527
|
+
var WIDTH_CACHE_MAX_TEXT_LENGTH = 4096;
|
|
7528
|
+
var widthCache = /* @__PURE__ */ new Map();
|
|
7524
7529
|
function visibleWidth(text) {
|
|
7525
|
-
if (
|
|
7526
|
-
return
|
|
7527
|
-
|
|
7530
|
+
if (/^[\x20-\x7e]*$/u.test(text))
|
|
7531
|
+
return text.length;
|
|
7532
|
+
const cached = widthCache.get(text);
|
|
7533
|
+
if (cached !== void 0)
|
|
7534
|
+
return cached;
|
|
7528
7535
|
const stripped = stripTerminalControls(text).replace(/\t/g, " ");
|
|
7529
7536
|
let width = 0;
|
|
7530
7537
|
for (const cluster of graphemeClusters(stripped)) {
|
|
7531
7538
|
width += clusterWidth(cluster);
|
|
7532
7539
|
}
|
|
7540
|
+
if (text.length <= WIDTH_CACHE_MAX_TEXT_LENGTH) {
|
|
7541
|
+
if (widthCache.size >= WIDTH_CACHE_SIZE) {
|
|
7542
|
+
const oldest = widthCache.keys().next().value;
|
|
7543
|
+
if (oldest !== void 0)
|
|
7544
|
+
widthCache.delete(oldest);
|
|
7545
|
+
}
|
|
7546
|
+
widthCache.set(text, width);
|
|
7547
|
+
}
|
|
7533
7548
|
return width;
|
|
7534
7549
|
}
|
|
7535
7550
|
function truncateToWidth(text, width) {
|
|
@@ -7783,7 +7798,7 @@ function truncateStyledLine(value, width, ellipsis = "\u2026") {
|
|
|
7783
7798
|
if (!truncated) {
|
|
7784
7799
|
return line(...mergeAdjacent(result));
|
|
7785
7800
|
}
|
|
7786
|
-
const ellipsisWidth = charCellWidth(ellipsis);
|
|
7801
|
+
const ellipsisWidth = ellipsis === "" ? 0 : charCellWidth(ellipsis);
|
|
7787
7802
|
while (result.length > 0 && used + ellipsisWidth > width) {
|
|
7788
7803
|
const removed = result.pop();
|
|
7789
7804
|
used -= charCellWidth(removed.text);
|
|
@@ -9161,6 +9176,10 @@ var UIEventReducer = class _UIEventReducer {
|
|
|
9161
9176
|
payload
|
|
9162
9177
|
});
|
|
9163
9178
|
}
|
|
9179
|
+
if (kind === "ui.context_usage") {
|
|
9180
|
+
this.#updateContextUsage(payload);
|
|
9181
|
+
return createUpdate(kind, { payload });
|
|
9182
|
+
}
|
|
9164
9183
|
if (kind === "task.state_changed") {
|
|
9165
9184
|
const rawState = payload.state ?? "IDLE";
|
|
9166
9185
|
this.state.sessionState = String(unwrapValue(rawState)).toUpperCase();
|
|
@@ -9761,6 +9780,23 @@ var TranscriptStore = class {
|
|
|
9761
9780
|
};
|
|
9762
9781
|
|
|
9763
9782
|
// ../../packages/terminal/tui/dist/tui/ansi-renderer.js
|
|
9783
|
+
var StyledLineCompiler = class {
|
|
9784
|
+
#cache = /* @__PURE__ */ new WeakMap();
|
|
9785
|
+
compile(values, width, theme) {
|
|
9786
|
+
return values.map((value) => {
|
|
9787
|
+
const cached = this.#cache.get(value);
|
|
9788
|
+
if (cached !== void 0 && cached.width === width && cached.theme === theme) {
|
|
9789
|
+
return cached.text;
|
|
9790
|
+
}
|
|
9791
|
+
const text = compileStyledLine(truncateStyledLine(value, width, ""), width, theme);
|
|
9792
|
+
this.#cache.set(value, { width, theme, text });
|
|
9793
|
+
return text;
|
|
9794
|
+
});
|
|
9795
|
+
}
|
|
9796
|
+
invalidate() {
|
|
9797
|
+
this.#cache = /* @__PURE__ */ new WeakMap();
|
|
9798
|
+
}
|
|
9799
|
+
};
|
|
9764
9800
|
function compileStyledLine(value, width, theme) {
|
|
9765
9801
|
const text = value.spans.map((item) => compileSpan(item, theme)).join("");
|
|
9766
9802
|
const renderedWidth = visibleWidth(text);
|
|
@@ -9821,7 +9857,7 @@ var CompletionPopup = class {
|
|
|
9821
9857
|
const description = item.description.replace(/[\r\n]+/gu, " ").trim();
|
|
9822
9858
|
const value = line(span(selected ? "\u203A " : " ", selected ? { foreground: "accent" } : void 0), span(item.value, selected ? { foreground: "accent" } : void 0), ...description ? [span(` ${description}`, { foreground: "muted" })] : []);
|
|
9823
9859
|
const boundedWidth = Math.max(1, width);
|
|
9824
|
-
return displayWidth2(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth
|
|
9860
|
+
return displayWidth2(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth, "");
|
|
9825
9861
|
}
|
|
9826
9862
|
};
|
|
9827
9863
|
function displayWidth2(value) {
|
|
@@ -9937,7 +9973,8 @@ function contextUsageLabel(tokens, contextWindow) {
|
|
|
9937
9973
|
return null;
|
|
9938
9974
|
}
|
|
9939
9975
|
const used = normalizedCount(tokens);
|
|
9940
|
-
const
|
|
9976
|
+
const ratio = Math.min(100, used / window * 100);
|
|
9977
|
+
const percent = ratio > 0 && ratio < 0.1 ? "<0.1" : formatScaled(ratio);
|
|
9941
9978
|
return `context: ${percent}% (${formatCompactCount(used)}/${formatCompactCount(window)})`;
|
|
9942
9979
|
}
|
|
9943
9980
|
function normalizedCount(value) {
|
|
@@ -9985,7 +10022,7 @@ function renderMarkdownStyledLines(text, width) {
|
|
|
9985
10022
|
const layoutWidth = Math.max(12, width);
|
|
9986
10023
|
const lines2 = [];
|
|
9987
10024
|
let previous = null;
|
|
9988
|
-
for (const block of renderBlocks(clean, layoutWidth)) {
|
|
10025
|
+
for (const block of renderBlocks(clean, layoutWidth, Math.max(1, width))) {
|
|
9989
10026
|
const leadingBlank = previous === null ? block.kind === "list" || block.kind === "table" || block.kind === "quote" : previous !== "hr";
|
|
9990
10027
|
if (leadingBlank) {
|
|
9991
10028
|
lines2.push(line());
|
|
@@ -10009,7 +10046,7 @@ function styleKey(style) {
|
|
|
10009
10046
|
style.background ?? ""
|
|
10010
10047
|
].join("|");
|
|
10011
10048
|
}
|
|
10012
|
-
function renderBlocks(text, layoutWidth) {
|
|
10049
|
+
function renderBlocks(text, layoutWidth, tableWidth) {
|
|
10013
10050
|
const lines2 = text.replace(/\r\n/g, "\n").split("\n");
|
|
10014
10051
|
const blocks = [];
|
|
10015
10052
|
let paragraph = [];
|
|
@@ -10057,6 +10094,7 @@ function renderBlocks(text, layoutWidth) {
|
|
|
10057
10094
|
const delimiter = splitTableRow(lines2[index + 1].trim());
|
|
10058
10095
|
if (header !== null && delimiter !== null && delimiter.length === header.length && delimiter.every((cell) => /^:?-+:?$/.test(cell))) {
|
|
10059
10096
|
flushParagraph();
|
|
10097
|
+
const tableStart = index;
|
|
10060
10098
|
index += 2;
|
|
10061
10099
|
const rows = [];
|
|
10062
10100
|
while (index < lines2.length) {
|
|
@@ -10073,7 +10111,7 @@ function renderBlocks(text, layoutWidth) {
|
|
|
10073
10111
|
}
|
|
10074
10112
|
blocks.push({
|
|
10075
10113
|
kind: "table",
|
|
10076
|
-
lines: renderTable(header, rows,
|
|
10114
|
+
lines: renderTable(header, rows, tableWidth, lines2.slice(tableStart, index))
|
|
10077
10115
|
});
|
|
10078
10116
|
continue;
|
|
10079
10117
|
}
|
|
@@ -10212,7 +10250,7 @@ function segmentsWidth(segments) {
|
|
|
10212
10250
|
}
|
|
10213
10251
|
return width;
|
|
10214
10252
|
}
|
|
10215
|
-
function renderTable(header, rows, layoutWidth) {
|
|
10253
|
+
function renderTable(header, rows, layoutWidth, rawLines) {
|
|
10216
10254
|
const columnCount = header.length;
|
|
10217
10255
|
const borderStyle = {
|
|
10218
10256
|
foreground: "border_muted"
|
|
@@ -10224,12 +10262,26 @@ function renderTable(header, rows, layoutWidth) {
|
|
|
10224
10262
|
const normalize = (cells) => Array.from({ length: columnCount }, (_, i) => cells[i] ?? "");
|
|
10225
10263
|
const headerCells = normalize(header).map((cell) => parseInline(cell, headerStyle));
|
|
10226
10264
|
const bodyCells = rows.map((row) => normalize(row).map((cell) => parseInline(cell, {})));
|
|
10265
|
+
const minWidths = Array(columnCount).fill(1);
|
|
10266
|
+
for (const row of [headerCells, ...bodyCells]) {
|
|
10267
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
10268
|
+
for (const segment of row[i]) {
|
|
10269
|
+
for (const cluster of graphemeClusters(segment.text)) {
|
|
10270
|
+
minWidths[i] = Math.max(minWidths[i], clusterWidth(cluster));
|
|
10271
|
+
}
|
|
10272
|
+
}
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
const spacingWidth = 2 * columnCount;
|
|
10276
|
+
if (minWidths.reduce((total, width) => total + width, spacingWidth) > layoutWidth) {
|
|
10277
|
+
return rawLines.flatMap((text) => wrapSegments([{ text, style: {} }], layoutWidth));
|
|
10278
|
+
}
|
|
10227
10279
|
const widths = Array.from({ length: columnCount }, (_, i) => Math.max(1, segmentsWidth(headerCells[i]), ...bodyCells.map((row) => segmentsWidth(row[i]))));
|
|
10228
10280
|
const totalWidth = () => widths.reduce((total, w) => total + w, 0) + 2 * (columnCount - 1) + 2;
|
|
10229
|
-
while (totalWidth() > layoutWidth
|
|
10230
|
-
let widest =
|
|
10231
|
-
for (let i =
|
|
10232
|
-
if (widths[i] > widths[widest]) {
|
|
10281
|
+
while (totalWidth() > layoutWidth) {
|
|
10282
|
+
let widest = -1;
|
|
10283
|
+
for (let i = 0; i < widths.length; i += 1) {
|
|
10284
|
+
if (widths[i] > minWidths[i] && (widest === -1 || widths[i] > widths[widest])) {
|
|
10233
10285
|
widest = i;
|
|
10234
10286
|
}
|
|
10235
10287
|
}
|
|
@@ -10259,7 +10311,7 @@ function renderTableRows(cells, widths, borderStyle, padStyle) {
|
|
|
10259
10311
|
if (i > 0) {
|
|
10260
10312
|
line2.push({ style: borderStyle, text: " " });
|
|
10261
10313
|
}
|
|
10262
|
-
const cellLine = wrappedCells[i][row];
|
|
10314
|
+
const cellLine = wrappedCells[i][row] ?? [];
|
|
10263
10315
|
line2.push(...cellLine);
|
|
10264
10316
|
const pad = widths[i] - segmentsWidth(cellLine);
|
|
10265
10317
|
if (pad > 0) {
|
|
@@ -10933,10 +10985,10 @@ var Transcript = class {
|
|
|
10933
10985
|
const key = cacheKey(block);
|
|
10934
10986
|
const signature = blockSignature(block);
|
|
10935
10987
|
const cached = this.#cache.get(key);
|
|
10936
|
-
if (cached !== void 0 && cached.width === context.width && cached.signature === signature) {
|
|
10988
|
+
if (cached !== void 0 && cached.block === block && cached.width === context.width && cached.signature === signature) {
|
|
10937
10989
|
return cached.lines;
|
|
10938
10990
|
}
|
|
10939
|
-
if (cached !== void 0 && cached.signature === signature) {
|
|
10991
|
+
if (cached !== void 0 && cached.block === block && cached.signature === signature) {
|
|
10940
10992
|
const lines3 = cached.component.render(context).lines;
|
|
10941
10993
|
this.#cache.set(key, {
|
|
10942
10994
|
...cached,
|
|
@@ -10948,6 +11000,7 @@ var Transcript = class {
|
|
|
10948
11000
|
const component = this.#createBlockComponent(block);
|
|
10949
11001
|
const lines2 = component.render(context).lines;
|
|
10950
11002
|
this.#cache.set(key, {
|
|
11003
|
+
block,
|
|
10951
11004
|
component,
|
|
10952
11005
|
width: context.width,
|
|
10953
11006
|
signature,
|
|
@@ -11096,6 +11149,11 @@ var FrameBuilder = class {
|
|
|
11096
11149
|
#effort;
|
|
11097
11150
|
#title;
|
|
11098
11151
|
#theme;
|
|
11152
|
+
#transcriptView;
|
|
11153
|
+
#lineCompiler = new StyledLineCompiler();
|
|
11154
|
+
#previousWidth = 0;
|
|
11155
|
+
#previousSources = [];
|
|
11156
|
+
#previousLines = [];
|
|
11099
11157
|
constructor(options) {
|
|
11100
11158
|
this.#state = options.state;
|
|
11101
11159
|
this.#transcript = options.transcript;
|
|
@@ -11105,12 +11163,21 @@ var FrameBuilder = class {
|
|
|
11105
11163
|
this.#effort = options.effort ?? null;
|
|
11106
11164
|
this.#title = options.title ?? "laoHuang";
|
|
11107
11165
|
this.#theme = options.theme ?? resolveTerminalTheme();
|
|
11166
|
+
this.#transcriptView = new Transcript({ blocks: this.#transcript.blocks() });
|
|
11108
11167
|
}
|
|
11109
11168
|
build(options) {
|
|
11110
11169
|
const terminalWidth = Math.max(1, options.width);
|
|
11111
11170
|
const width = Math.max(1, terminalWidth - 1);
|
|
11112
11171
|
const mainScreen = options.compiledMainScreen ?? this.#fallbackMainScreen(options, width);
|
|
11113
|
-
const lines2 = mainScreen.lines.map((value) =>
|
|
11172
|
+
const lines2 = mainScreen.lines.map((value, index) => {
|
|
11173
|
+
if (this.#previousWidth === width && this.#previousSources[index] === value) {
|
|
11174
|
+
return this.#previousLines[index];
|
|
11175
|
+
}
|
|
11176
|
+
return visibleWidth(value) <= width ? value : truncateToWidth(value, width);
|
|
11177
|
+
});
|
|
11178
|
+
this.#previousWidth = width;
|
|
11179
|
+
this.#previousSources = [...mainScreen.lines];
|
|
11180
|
+
this.#previousLines = lines2;
|
|
11114
11181
|
const cursor = {
|
|
11115
11182
|
row: Math.max(0, Math.min(mainScreen.cursor.row, Math.max(0, lines2.length - 1))),
|
|
11116
11183
|
col: Math.max(0, Math.min(mainScreen.cursor.column, width - 1))
|
|
@@ -11132,9 +11199,7 @@ var FrameBuilder = class {
|
|
|
11132
11199
|
return this.#status().render({ width: Math.max(1, width), theme: this.#theme }).lines.map(lineText).join("\n");
|
|
11133
11200
|
}
|
|
11134
11201
|
#fallbackMainScreen(options, width) {
|
|
11135
|
-
const transcript =
|
|
11136
|
-
blocks: this.#transcript.blocks()
|
|
11137
|
-
}).renderWithMetadata({ width, theme: this.#theme });
|
|
11202
|
+
const transcript = this.#transcriptView.renderWithMetadata({ width, theme: this.#theme });
|
|
11138
11203
|
const transcriptLines = transcript.lines;
|
|
11139
11204
|
const composer = new Composer({
|
|
11140
11205
|
editor: options.editor,
|
|
@@ -11149,7 +11214,7 @@ var FrameBuilder = class {
|
|
|
11149
11214
|
const cursor = composer.cursor ?? { row: Math.max(0, composer.lines.length - 1), column: 0 };
|
|
11150
11215
|
const lines2 = [...transcriptLines, ...composer.lines, ...completion.lines, ...status.lines];
|
|
11151
11216
|
return {
|
|
11152
|
-
lines:
|
|
11217
|
+
lines: this.#lineCompiler.compile(lines2, width, this.#theme),
|
|
11153
11218
|
cursor: {
|
|
11154
11219
|
row: transcriptLines.length + cursor.row,
|
|
11155
11220
|
column: cursor.column
|
|
@@ -12641,6 +12706,7 @@ var TerminalUI = class {
|
|
|
12641
12706
|
#loop = null;
|
|
12642
12707
|
#transcript;
|
|
12643
12708
|
#transcriptView;
|
|
12709
|
+
#lineCompiler = new StyledLineCompiler();
|
|
12644
12710
|
#showReasoning = true;
|
|
12645
12711
|
#displayPolicy = new DisplayPolicy({
|
|
12646
12712
|
audience: "terminal",
|
|
@@ -12772,9 +12838,16 @@ var TerminalUI = class {
|
|
|
12772
12838
|
this.#sessionId = sessionId;
|
|
12773
12839
|
this.#loop?.requestRender();
|
|
12774
12840
|
}
|
|
12841
|
+
setContextUsage(tokens, contextWindow) {
|
|
12842
|
+
this.publishEvent({
|
|
12843
|
+
kind: "ui.context_usage",
|
|
12844
|
+
payload: { context_tokens: tokens, context_window: contextWindow }
|
|
12845
|
+
});
|
|
12846
|
+
}
|
|
12775
12847
|
replaceTranscript(items) {
|
|
12776
12848
|
this.#transcript.replace(items);
|
|
12777
12849
|
this.#transcriptView.invalidate();
|
|
12850
|
+
this.#lineCompiler.invalidate();
|
|
12778
12851
|
this.#loop?.requestRender();
|
|
12779
12852
|
}
|
|
12780
12853
|
setComposerText(text) {
|
|
@@ -12900,7 +12973,7 @@ var TerminalUI = class {
|
|
|
12900
12973
|
#buildHistoryFrameParts(width) {
|
|
12901
12974
|
const rendered = this.#transcriptView.renderWithMetadata({ width, theme: this.theme });
|
|
12902
12975
|
return {
|
|
12903
|
-
lines:
|
|
12976
|
+
lines: this.#lineCompiler.compile(rendered.lines, Math.max(12, width), this.theme),
|
|
12904
12977
|
activeStart: rendered.activeStart
|
|
12905
12978
|
};
|
|
12906
12979
|
}
|
|
@@ -12931,7 +13004,7 @@ var TerminalUI = class {
|
|
|
12931
13004
|
effort: this.effort
|
|
12932
13005
|
})
|
|
12933
13006
|
}).renderWithMetadata({ width: contentWidth, theme: this.theme });
|
|
12934
|
-
const lines2 =
|
|
13007
|
+
const lines2 = this.#lineCompiler.compile(rendered.lines, contentWidth, this.theme);
|
|
12935
13008
|
return this.#frameBuilder.build({
|
|
12936
13009
|
...options,
|
|
12937
13010
|
compiledMainScreen: {
|
|
@@ -14679,7 +14752,7 @@ var SessionCommands = class {
|
|
|
14679
14752
|
this.registry = new CommandRegistry([
|
|
14680
14753
|
{
|
|
14681
14754
|
name: "/model",
|
|
14682
|
-
description: "\u9009\u62E9\
|
|
14755
|
+
description: "\u641C\u7D22\u5E76\u9009\u62E9\u5DF2\u914D\u7F6E\u4F9B\u5E94\u5546\u7684\u6A21\u578B",
|
|
14683
14756
|
usage: "/model [provider|model] [model]",
|
|
14684
14757
|
handler: (args) => this.handleModel(args),
|
|
14685
14758
|
allowedStates: IDLE_ONLY,
|
|
@@ -15066,16 +15139,10 @@ ${sessionDisplayDescription(session, {
|
|
|
15066
15139
|
}
|
|
15067
15140
|
let provider;
|
|
15068
15141
|
let modelName;
|
|
15069
|
-
if (args.length ===
|
|
15070
|
-
const selectedProvider = await this.selectModelProvider();
|
|
15071
|
-
if (selectedProvider === null) {
|
|
15072
|
-
return true;
|
|
15073
|
-
}
|
|
15074
|
-
provider = selectedProvider;
|
|
15075
|
-
} else if (args.length === 2) {
|
|
15142
|
+
if (args.length === 2) {
|
|
15076
15143
|
provider = args[0];
|
|
15077
15144
|
modelName = args[1];
|
|
15078
|
-
} else {
|
|
15145
|
+
} else if (args.length === 1) {
|
|
15079
15146
|
const argument = args[0];
|
|
15080
15147
|
if (this.#catalog.getProvider(argument) !== void 0) {
|
|
15081
15148
|
provider = argument;
|
|
@@ -15086,9 +15153,24 @@ ${sessionDisplayDescription(session, {
|
|
|
15086
15153
|
}
|
|
15087
15154
|
if (modelName === void 0) {
|
|
15088
15155
|
try {
|
|
15089
|
-
|
|
15156
|
+
let models;
|
|
15157
|
+
if (provider === void 0) {
|
|
15158
|
+
const available = await this.#selector.listConfiguredModels();
|
|
15159
|
+
models = available.models;
|
|
15160
|
+
for (const failure of available.errors) {
|
|
15161
|
+
this.notice(
|
|
15162
|
+
`Could not list models for ${failure.provider}: ${errorMessage10(failure.error)}`,
|
|
15163
|
+
"warning"
|
|
15164
|
+
);
|
|
15165
|
+
}
|
|
15166
|
+
} else {
|
|
15167
|
+
models = await this.#selector.listModels(provider, "");
|
|
15168
|
+
}
|
|
15090
15169
|
if (models.length === 0) {
|
|
15091
|
-
this.notice(
|
|
15170
|
+
this.notice(
|
|
15171
|
+
provider === void 0 ? "No models available from configured providers. Use /login to configure a provider." : `No models available for provider: ${provider}. Use /login ${provider} to configure credentials.`,
|
|
15172
|
+
"warning"
|
|
15173
|
+
);
|
|
15092
15174
|
return true;
|
|
15093
15175
|
}
|
|
15094
15176
|
if (this.#presenter === null) {
|
|
@@ -15097,26 +15179,34 @@ ${sessionDisplayDescription(session, {
|
|
|
15097
15179
|
}
|
|
15098
15180
|
const selected = await this.#presenter.select({
|
|
15099
15181
|
id: "model-name",
|
|
15100
|
-
title: `Select model for ${provider}`,
|
|
15101
|
-
items: models.map((
|
|
15102
|
-
value: `${provider}/${
|
|
15103
|
-
label:
|
|
15104
|
-
description: provider
|
|
15182
|
+
title: provider === void 0 ? "Select model from configured providers" : `Select model for ${provider}`,
|
|
15183
|
+
items: models.map((model2) => ({
|
|
15184
|
+
value: `${model2.provider}/${model2.id}`,
|
|
15185
|
+
label: model2.name,
|
|
15186
|
+
description: model2.provider
|
|
15105
15187
|
})),
|
|
15106
|
-
currentValue: this.#currentConfig.provider
|
|
15188
|
+
currentValue: `${this.#currentConfig.provider}/${this.#currentConfig.model}`,
|
|
15107
15189
|
searchable: true,
|
|
15108
|
-
maxVisible:
|
|
15190
|
+
maxVisible: 10
|
|
15109
15191
|
});
|
|
15110
15192
|
if (selected === null) {
|
|
15111
15193
|
return true;
|
|
15112
15194
|
}
|
|
15113
|
-
const
|
|
15114
|
-
|
|
15195
|
+
const model = models.find((candidate) => `${candidate.provider}/${candidate.id}` === selected);
|
|
15196
|
+
if (model === void 0) {
|
|
15197
|
+
this.notice("Selected model is no longer available. Run /model to refresh the list.", "error");
|
|
15198
|
+
return true;
|
|
15199
|
+
}
|
|
15200
|
+
provider = model.provider;
|
|
15201
|
+
modelName = model.id;
|
|
15115
15202
|
} catch (error) {
|
|
15116
15203
|
this.notice(`Could not list models: ${errorMessage10(error)}`, "error");
|
|
15117
15204
|
return true;
|
|
15118
15205
|
}
|
|
15119
15206
|
}
|
|
15207
|
+
if (provider === void 0) {
|
|
15208
|
+
return true;
|
|
15209
|
+
}
|
|
15120
15210
|
let selection;
|
|
15121
15211
|
try {
|
|
15122
15212
|
selection = await this.#selector.selectExact({
|
|
@@ -15129,6 +15219,7 @@ ${sessionDisplayDescription(session, {
|
|
|
15129
15219
|
return true;
|
|
15130
15220
|
}
|
|
15131
15221
|
if (selection === null) {
|
|
15222
|
+
this.notice(`Use /login ${provider} to configure credentials before switching models.`, "warning");
|
|
15132
15223
|
return true;
|
|
15133
15224
|
}
|
|
15134
15225
|
const previousProvider = this.#currentConfig.provider;
|
|
@@ -15387,23 +15478,6 @@ ${sessionDisplayDescription(session, {
|
|
|
15387
15478
|
)
|
|
15388
15479
|
};
|
|
15389
15480
|
}
|
|
15390
|
-
async selectModelProvider() {
|
|
15391
|
-
if (this.#presenter === null) {
|
|
15392
|
-
this.notice("Model provider selection is unavailable.", "error");
|
|
15393
|
-
return null;
|
|
15394
|
-
}
|
|
15395
|
-
const selected = await this.#presenter.select({
|
|
15396
|
-
id: "model-provider",
|
|
15397
|
-
title: "Select model provider",
|
|
15398
|
-
items: this.#selector.listProviders().map((provider) => ({
|
|
15399
|
-
value: provider.id,
|
|
15400
|
-
label: provider.name,
|
|
15401
|
-
description: provider.id
|
|
15402
|
-
})),
|
|
15403
|
-
currentValue: this.#currentConfig.provider
|
|
15404
|
-
});
|
|
15405
|
-
return selected;
|
|
15406
|
-
}
|
|
15407
15481
|
*modelCompletions(args) {
|
|
15408
15482
|
if (args.length === 0) {
|
|
15409
15483
|
yield ["current", "\u663E\u793A\u5F53\u524D\u6A21\u578B"];
|
|
@@ -15520,13 +15594,31 @@ var ModelSelector = class {
|
|
|
15520
15594
|
return this.#catalog.listProviders();
|
|
15521
15595
|
}
|
|
15522
15596
|
async listModels(provider, query) {
|
|
15597
|
+
if (!await this.#providerAuth.ensureConfigured(provider, { promptIfMissing: false })) {
|
|
15598
|
+
return [];
|
|
15599
|
+
}
|
|
15523
15600
|
await this.#catalog.refresh(provider);
|
|
15524
15601
|
return filterModels(
|
|
15525
15602
|
await this.#catalog.listAvailableModels(provider),
|
|
15526
|
-
query
|
|
15527
|
-
20
|
|
15603
|
+
query
|
|
15528
15604
|
);
|
|
15529
15605
|
}
|
|
15606
|
+
async listConfiguredModels() {
|
|
15607
|
+
const providers = this.#catalog.listProviders();
|
|
15608
|
+
const results = await Promise.allSettled(
|
|
15609
|
+
providers.map((provider) => this.listModels(provider.id, ""))
|
|
15610
|
+
);
|
|
15611
|
+
const models = [];
|
|
15612
|
+
const errors = [];
|
|
15613
|
+
for (const [index, result] of results.entries()) {
|
|
15614
|
+
if (result.status === "fulfilled") {
|
|
15615
|
+
models.push(...result.value);
|
|
15616
|
+
} else {
|
|
15617
|
+
errors.push({ provider: providers[index].id, error: result.reason });
|
|
15618
|
+
}
|
|
15619
|
+
}
|
|
15620
|
+
return { models: filterModels(models, ""), errors };
|
|
15621
|
+
}
|
|
15530
15622
|
async selectExact(options) {
|
|
15531
15623
|
const provider = this.#catalog.getProvider(options.providerName);
|
|
15532
15624
|
if (provider === void 0) {
|
|
@@ -15557,7 +15649,7 @@ var ModelSelector = class {
|
|
|
15557
15649
|
};
|
|
15558
15650
|
}
|
|
15559
15651
|
};
|
|
15560
|
-
function filterModels(models, query
|
|
15652
|
+
function filterModels(models, query) {
|
|
15561
15653
|
const normalized = query.toLowerCase().trim();
|
|
15562
15654
|
const terms = normalized.split(/\s+/).filter((term) => term.length > 0);
|
|
15563
15655
|
const matched = models.filter((model) => {
|
|
@@ -15581,9 +15673,9 @@ function filterModels(models, query, limit = 20) {
|
|
|
15581
15673
|
if (leftPrefix !== rightPrefix) {
|
|
15582
15674
|
return leftPrefix ? -1 : 1;
|
|
15583
15675
|
}
|
|
15584
|
-
return left.id.localeCompare(right.id);
|
|
15676
|
+
return left.provider.localeCompare(right.provider) || left.id.localeCompare(right.id);
|
|
15585
15677
|
});
|
|
15586
|
-
return matched
|
|
15678
|
+
return matched;
|
|
15587
15679
|
}
|
|
15588
15680
|
|
|
15589
15681
|
// src/plain-command-presenter.ts
|
|
@@ -16668,6 +16760,15 @@ function resetEmptySessionTranscript(terminalUi) {
|
|
|
16668
16760
|
terminalUi?.replaceTranscript([]);
|
|
16669
16761
|
terminalUi?.showWelcome();
|
|
16670
16762
|
}
|
|
16763
|
+
function refreshSessionContextUsage(terminalUi, input) {
|
|
16764
|
+
if (terminalUi === null) return;
|
|
16765
|
+
const context = new ContextBuilder().build(input);
|
|
16766
|
+
const estimator = new DefaultTokenEstimator();
|
|
16767
|
+
terminalUi.setContextUsage(
|
|
16768
|
+
estimator.estimateMessages(context.messages) + estimator.estimateTools(input.tools),
|
|
16769
|
+
input.contextWindow
|
|
16770
|
+
);
|
|
16771
|
+
}
|
|
16671
16772
|
async function main(argv, options = {}) {
|
|
16672
16773
|
let parsed;
|
|
16673
16774
|
try {
|
|
@@ -17105,6 +17206,16 @@ async function main(argv, options = {}) {
|
|
|
17105
17206
|
input: presenterInput,
|
|
17106
17207
|
secretInput: presenterSecretInput
|
|
17107
17208
|
}) : new TerminalCommandPresenter(terminalUi);
|
|
17209
|
+
const refreshContextUsage = () => {
|
|
17210
|
+
refreshSessionContextUsage(terminalUi, {
|
|
17211
|
+
entries: sessionController.history?.entries() ?? [],
|
|
17212
|
+
currentProvider: config.provider,
|
|
17213
|
+
currentModel: config.model,
|
|
17214
|
+
tools: toolRegistry.definitions,
|
|
17215
|
+
contextWindow: selectedModel?.contextWindow ?? 0
|
|
17216
|
+
});
|
|
17217
|
+
};
|
|
17218
|
+
refreshContextUsage();
|
|
17108
17219
|
const refreshSessionView = () => {
|
|
17109
17220
|
const currentSessionId = sessionController.currentSessionId;
|
|
17110
17221
|
if (currentSessionId !== null) {
|
|
@@ -17126,6 +17237,7 @@ async function main(argv, options = {}) {
|
|
|
17126
17237
|
agent.messages = [system];
|
|
17127
17238
|
}
|
|
17128
17239
|
resetEmptySessionTranscript(terminalUi);
|
|
17240
|
+
refreshContextUsage();
|
|
17129
17241
|
return;
|
|
17130
17242
|
}
|
|
17131
17243
|
agent.messages = [...new ContextBuilder().build({
|
|
@@ -17134,6 +17246,7 @@ async function main(argv, options = {}) {
|
|
|
17134
17246
|
currentModel: config.model
|
|
17135
17247
|
}).messages];
|
|
17136
17248
|
terminalUi?.replaceTranscript(projectTranscript(entries));
|
|
17249
|
+
refreshContextUsage();
|
|
17137
17250
|
};
|
|
17138
17251
|
const commands = new SessionCommands({
|
|
17139
17252
|
agent,
|
|
@@ -17173,6 +17286,7 @@ async function main(argv, options = {}) {
|
|
|
17173
17286
|
terminalUi.state.model = selection.config.model;
|
|
17174
17287
|
terminalUi.setRuntimeCapabilities({ reasoning: model?.reasoning ?? false });
|
|
17175
17288
|
}
|
|
17289
|
+
refreshContextUsage();
|
|
17176
17290
|
}
|
|
17177
17291
|
});
|
|
17178
17292
|
const unsubscribers = [];
|
|
@@ -17180,6 +17294,9 @@ async function main(argv, options = {}) {
|
|
|
17180
17294
|
unsubscribers.push(
|
|
17181
17295
|
runtime.eventBus.subscribe((event) => {
|
|
17182
17296
|
sessionSink.publishEvent(projector.project(event, "terminal"));
|
|
17297
|
+
if (event.session_id === runtime.sessionId && (event.kind === "task.completed" || event.kind === "task.cancelled" || event.kind === "task.failed")) {
|
|
17298
|
+
refreshContextUsage();
|
|
17299
|
+
}
|
|
17183
17300
|
})
|
|
17184
17301
|
);
|
|
17185
17302
|
if (terminalUi !== null) {
|
|
@@ -17240,6 +17357,10 @@ async function main(argv, options = {}) {
|
|
|
17240
17357
|
await sessionRecorder.close();
|
|
17241
17358
|
await sessionController.close();
|
|
17242
17359
|
}
|
|
17360
|
+
const renderError = terminalUi?.renderError ?? null;
|
|
17361
|
+
if (renderError !== null) {
|
|
17362
|
+
throw new Error(`Terminal rendering failed: ${errorMessage11(renderError)}`, { cause: renderError });
|
|
17363
|
+
}
|
|
17243
17364
|
return cleanShutdown ? 0 : 1;
|
|
17244
17365
|
}
|
|
17245
17366
|
function defaultSessionsRoot(environ) {
|