laohuang 0.8.1 → 0.8.2
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/dist/bin.js +100 -17
- package/dist/bin.js.map +2 -2
- package/package.json +1 -1
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) {
|
|
@@ -10933,10 +10970,10 @@ var Transcript = class {
|
|
|
10933
10970
|
const key = cacheKey(block);
|
|
10934
10971
|
const signature = blockSignature(block);
|
|
10935
10972
|
const cached = this.#cache.get(key);
|
|
10936
|
-
if (cached !== void 0 && cached.width === context.width && cached.signature === signature) {
|
|
10973
|
+
if (cached !== void 0 && cached.block === block && cached.width === context.width && cached.signature === signature) {
|
|
10937
10974
|
return cached.lines;
|
|
10938
10975
|
}
|
|
10939
|
-
if (cached !== void 0 && cached.signature === signature) {
|
|
10976
|
+
if (cached !== void 0 && cached.block === block && cached.signature === signature) {
|
|
10940
10977
|
const lines3 = cached.component.render(context).lines;
|
|
10941
10978
|
this.#cache.set(key, {
|
|
10942
10979
|
...cached,
|
|
@@ -10948,6 +10985,7 @@ var Transcript = class {
|
|
|
10948
10985
|
const component = this.#createBlockComponent(block);
|
|
10949
10986
|
const lines2 = component.render(context).lines;
|
|
10950
10987
|
this.#cache.set(key, {
|
|
10988
|
+
block,
|
|
10951
10989
|
component,
|
|
10952
10990
|
width: context.width,
|
|
10953
10991
|
signature,
|
|
@@ -11096,6 +11134,11 @@ var FrameBuilder = class {
|
|
|
11096
11134
|
#effort;
|
|
11097
11135
|
#title;
|
|
11098
11136
|
#theme;
|
|
11137
|
+
#transcriptView;
|
|
11138
|
+
#lineCompiler = new StyledLineCompiler();
|
|
11139
|
+
#previousWidth = 0;
|
|
11140
|
+
#previousSources = [];
|
|
11141
|
+
#previousLines = [];
|
|
11099
11142
|
constructor(options) {
|
|
11100
11143
|
this.#state = options.state;
|
|
11101
11144
|
this.#transcript = options.transcript;
|
|
@@ -11105,12 +11148,21 @@ var FrameBuilder = class {
|
|
|
11105
11148
|
this.#effort = options.effort ?? null;
|
|
11106
11149
|
this.#title = options.title ?? "laoHuang";
|
|
11107
11150
|
this.#theme = options.theme ?? resolveTerminalTheme();
|
|
11151
|
+
this.#transcriptView = new Transcript({ blocks: this.#transcript.blocks() });
|
|
11108
11152
|
}
|
|
11109
11153
|
build(options) {
|
|
11110
11154
|
const terminalWidth = Math.max(1, options.width);
|
|
11111
11155
|
const width = Math.max(1, terminalWidth - 1);
|
|
11112
11156
|
const mainScreen = options.compiledMainScreen ?? this.#fallbackMainScreen(options, width);
|
|
11113
|
-
const lines2 = mainScreen.lines.map((value) =>
|
|
11157
|
+
const lines2 = mainScreen.lines.map((value, index) => {
|
|
11158
|
+
if (this.#previousWidth === width && this.#previousSources[index] === value) {
|
|
11159
|
+
return this.#previousLines[index];
|
|
11160
|
+
}
|
|
11161
|
+
return visibleWidth(value) <= width ? value : truncateToWidth(value, width);
|
|
11162
|
+
});
|
|
11163
|
+
this.#previousWidth = width;
|
|
11164
|
+
this.#previousSources = [...mainScreen.lines];
|
|
11165
|
+
this.#previousLines = lines2;
|
|
11114
11166
|
const cursor = {
|
|
11115
11167
|
row: Math.max(0, Math.min(mainScreen.cursor.row, Math.max(0, lines2.length - 1))),
|
|
11116
11168
|
col: Math.max(0, Math.min(mainScreen.cursor.column, width - 1))
|
|
@@ -11132,9 +11184,7 @@ var FrameBuilder = class {
|
|
|
11132
11184
|
return this.#status().render({ width: Math.max(1, width), theme: this.#theme }).lines.map(lineText).join("\n");
|
|
11133
11185
|
}
|
|
11134
11186
|
#fallbackMainScreen(options, width) {
|
|
11135
|
-
const transcript =
|
|
11136
|
-
blocks: this.#transcript.blocks()
|
|
11137
|
-
}).renderWithMetadata({ width, theme: this.#theme });
|
|
11187
|
+
const transcript = this.#transcriptView.renderWithMetadata({ width, theme: this.#theme });
|
|
11138
11188
|
const transcriptLines = transcript.lines;
|
|
11139
11189
|
const composer = new Composer({
|
|
11140
11190
|
editor: options.editor,
|
|
@@ -11149,7 +11199,7 @@ var FrameBuilder = class {
|
|
|
11149
11199
|
const cursor = composer.cursor ?? { row: Math.max(0, composer.lines.length - 1), column: 0 };
|
|
11150
11200
|
const lines2 = [...transcriptLines, ...composer.lines, ...completion.lines, ...status.lines];
|
|
11151
11201
|
return {
|
|
11152
|
-
lines:
|
|
11202
|
+
lines: this.#lineCompiler.compile(lines2, width, this.#theme),
|
|
11153
11203
|
cursor: {
|
|
11154
11204
|
row: transcriptLines.length + cursor.row,
|
|
11155
11205
|
column: cursor.column
|
|
@@ -12641,6 +12691,7 @@ var TerminalUI = class {
|
|
|
12641
12691
|
#loop = null;
|
|
12642
12692
|
#transcript;
|
|
12643
12693
|
#transcriptView;
|
|
12694
|
+
#lineCompiler = new StyledLineCompiler();
|
|
12644
12695
|
#showReasoning = true;
|
|
12645
12696
|
#displayPolicy = new DisplayPolicy({
|
|
12646
12697
|
audience: "terminal",
|
|
@@ -12772,9 +12823,16 @@ var TerminalUI = class {
|
|
|
12772
12823
|
this.#sessionId = sessionId;
|
|
12773
12824
|
this.#loop?.requestRender();
|
|
12774
12825
|
}
|
|
12826
|
+
setContextUsage(tokens, contextWindow) {
|
|
12827
|
+
this.publishEvent({
|
|
12828
|
+
kind: "ui.context_usage",
|
|
12829
|
+
payload: { context_tokens: tokens, context_window: contextWindow }
|
|
12830
|
+
});
|
|
12831
|
+
}
|
|
12775
12832
|
replaceTranscript(items) {
|
|
12776
12833
|
this.#transcript.replace(items);
|
|
12777
12834
|
this.#transcriptView.invalidate();
|
|
12835
|
+
this.#lineCompiler.invalidate();
|
|
12778
12836
|
this.#loop?.requestRender();
|
|
12779
12837
|
}
|
|
12780
12838
|
setComposerText(text) {
|
|
@@ -12900,7 +12958,7 @@ var TerminalUI = class {
|
|
|
12900
12958
|
#buildHistoryFrameParts(width) {
|
|
12901
12959
|
const rendered = this.#transcriptView.renderWithMetadata({ width, theme: this.theme });
|
|
12902
12960
|
return {
|
|
12903
|
-
lines:
|
|
12961
|
+
lines: this.#lineCompiler.compile(rendered.lines, Math.max(12, width), this.theme),
|
|
12904
12962
|
activeStart: rendered.activeStart
|
|
12905
12963
|
};
|
|
12906
12964
|
}
|
|
@@ -12931,7 +12989,7 @@ var TerminalUI = class {
|
|
|
12931
12989
|
effort: this.effort
|
|
12932
12990
|
})
|
|
12933
12991
|
}).renderWithMetadata({ width: contentWidth, theme: this.theme });
|
|
12934
|
-
const lines2 =
|
|
12992
|
+
const lines2 = this.#lineCompiler.compile(rendered.lines, contentWidth, this.theme);
|
|
12935
12993
|
return this.#frameBuilder.build({
|
|
12936
12994
|
...options,
|
|
12937
12995
|
compiledMainScreen: {
|
|
@@ -16668,6 +16726,15 @@ function resetEmptySessionTranscript(terminalUi) {
|
|
|
16668
16726
|
terminalUi?.replaceTranscript([]);
|
|
16669
16727
|
terminalUi?.showWelcome();
|
|
16670
16728
|
}
|
|
16729
|
+
function refreshSessionContextUsage(terminalUi, input) {
|
|
16730
|
+
if (terminalUi === null) return;
|
|
16731
|
+
const context = new ContextBuilder().build(input);
|
|
16732
|
+
const estimator = new DefaultTokenEstimator();
|
|
16733
|
+
terminalUi.setContextUsage(
|
|
16734
|
+
estimator.estimateMessages(context.messages) + estimator.estimateTools(input.tools),
|
|
16735
|
+
input.contextWindow
|
|
16736
|
+
);
|
|
16737
|
+
}
|
|
16671
16738
|
async function main(argv, options = {}) {
|
|
16672
16739
|
let parsed;
|
|
16673
16740
|
try {
|
|
@@ -17105,6 +17172,16 @@ async function main(argv, options = {}) {
|
|
|
17105
17172
|
input: presenterInput,
|
|
17106
17173
|
secretInput: presenterSecretInput
|
|
17107
17174
|
}) : new TerminalCommandPresenter(terminalUi);
|
|
17175
|
+
const refreshContextUsage = () => {
|
|
17176
|
+
refreshSessionContextUsage(terminalUi, {
|
|
17177
|
+
entries: sessionController.history?.entries() ?? [],
|
|
17178
|
+
currentProvider: config.provider,
|
|
17179
|
+
currentModel: config.model,
|
|
17180
|
+
tools: toolRegistry.definitions,
|
|
17181
|
+
contextWindow: selectedModel?.contextWindow ?? 0
|
|
17182
|
+
});
|
|
17183
|
+
};
|
|
17184
|
+
refreshContextUsage();
|
|
17108
17185
|
const refreshSessionView = () => {
|
|
17109
17186
|
const currentSessionId = sessionController.currentSessionId;
|
|
17110
17187
|
if (currentSessionId !== null) {
|
|
@@ -17126,6 +17203,7 @@ async function main(argv, options = {}) {
|
|
|
17126
17203
|
agent.messages = [system];
|
|
17127
17204
|
}
|
|
17128
17205
|
resetEmptySessionTranscript(terminalUi);
|
|
17206
|
+
refreshContextUsage();
|
|
17129
17207
|
return;
|
|
17130
17208
|
}
|
|
17131
17209
|
agent.messages = [...new ContextBuilder().build({
|
|
@@ -17134,6 +17212,7 @@ async function main(argv, options = {}) {
|
|
|
17134
17212
|
currentModel: config.model
|
|
17135
17213
|
}).messages];
|
|
17136
17214
|
terminalUi?.replaceTranscript(projectTranscript(entries));
|
|
17215
|
+
refreshContextUsage();
|
|
17137
17216
|
};
|
|
17138
17217
|
const commands = new SessionCommands({
|
|
17139
17218
|
agent,
|
|
@@ -17173,6 +17252,7 @@ async function main(argv, options = {}) {
|
|
|
17173
17252
|
terminalUi.state.model = selection.config.model;
|
|
17174
17253
|
terminalUi.setRuntimeCapabilities({ reasoning: model?.reasoning ?? false });
|
|
17175
17254
|
}
|
|
17255
|
+
refreshContextUsage();
|
|
17176
17256
|
}
|
|
17177
17257
|
});
|
|
17178
17258
|
const unsubscribers = [];
|
|
@@ -17180,6 +17260,9 @@ async function main(argv, options = {}) {
|
|
|
17180
17260
|
unsubscribers.push(
|
|
17181
17261
|
runtime.eventBus.subscribe((event) => {
|
|
17182
17262
|
sessionSink.publishEvent(projector.project(event, "terminal"));
|
|
17263
|
+
if (event.session_id === runtime.sessionId && (event.kind === "task.completed" || event.kind === "task.cancelled" || event.kind === "task.failed")) {
|
|
17264
|
+
refreshContextUsage();
|
|
17265
|
+
}
|
|
17183
17266
|
})
|
|
17184
17267
|
);
|
|
17185
17268
|
if (terminalUi !== null) {
|