mixdog 0.9.53 → 0.9.55
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/package.json +2 -1
- package/scripts/agent-model-liveness-test.mjs +68 -0
- package/scripts/anthropic-transport-policy-test.mjs +260 -0
- package/scripts/desktop-session-bridge-test.mjs +47 -0
- package/scripts/gemini-provider-test.mjs +396 -1
- package/scripts/grok-oauth-refresh-race-test.mjs +76 -1
- package/scripts/interrupted-turn-history-test.mjs +28 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +81 -1
- package/scripts/pending-completion-drop-test.mjs +160 -4
- package/scripts/pending-messages-lock-nonblocking-test.mjs +62 -0
- package/scripts/process-lifecycle-test.mjs +18 -4
- package/scripts/prompt-input-parity-test.mjs +145 -0
- package/scripts/provider-admission-scheduler-test.mjs +4 -5
- package/scripts/provider-contract-test.mjs +91 -33
- package/scripts/provider-toolcall-test.mjs +97 -17
- package/scripts/streaming-tail-window-test.mjs +146 -0
- package/scripts/tui-runtime-load-bench-entry.jsx +608 -0
- package/scripts/tui-runtime-load-bench.mjs +45 -0
- package/scripts/tui-store-frame-batch-test.mjs +99 -0
- package/scripts/tui-transcript-perf-test.mjs +367 -2
- package/scripts/write-backpressure-test.mjs +147 -0
- package/src/cli.mjs +5 -5
- package/src/lib/keychain-cjs.cjs +114 -25
- package/src/repl.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -25
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +50 -23
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +136 -27
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +104 -20
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +96 -75
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +40 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +49 -9
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +53 -13
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +145 -23
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +20 -4
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +316 -63
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +46 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +6 -13
- package/src/runtime/memory/lib/trace-store.mjs +66 -4
- package/src/runtime/shared/buffered-appender.mjs +83 -4
- package/src/runtime/shared/memory-snapshot.mjs +45 -36
- package/src/runtime/shared/process-lifecycle.mjs +166 -45
- package/src/runtime/shared/process-shutdown.mjs +2 -1
- package/src/session-runtime/model-route-api.mjs +4 -1
- package/src/session-runtime/native-search.mjs +4 -2
- package/src/session-runtime/provider-auth-api.mjs +8 -1
- package/src/session-runtime/provider-models.mjs +8 -3
- package/src/session-runtime/resource-api.mjs +2 -1
- package/src/session-runtime/runtime-core.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/warmup-schedulers.mjs +5 -1
- package/src/standalone/agent-tool.mjs +19 -1
- package/src/tui/App.jsx +10 -4
- package/src/tui/app/text-layout.mjs +9 -1
- package/src/tui/app/transcript-window.mjs +146 -60
- package/src/tui/app/use-mouse-input.mjs +16 -8
- package/src/tui/app/use-transcript-scroll.mjs +71 -19
- package/src/tui/app/use-transcript-window.mjs +21 -10
- package/src/tui/components/PromptInput.jsx +13 -6
- package/src/tui/dist/index.mjs +1094 -232
- package/src/tui/engine/context-state.mjs +31 -31
- package/src/tui/engine/frame-batched-store.mjs +75 -0
- package/src/tui/engine/session-api-ext.mjs +6 -1
- package/src/tui/engine/session-api.mjs +9 -3
- package/src/tui/engine/session-flow.mjs +54 -27
- package/src/tui/engine/turn.mjs +44 -3
- package/src/tui/engine.mjs +515 -36
- package/src/tui/input-editing.mjs +33 -13
- package/src/tui/markdown/measure-rendered-rows.mjs +117 -5
- package/vendor/ink/build/ink.js +8 -16
package/src/tui/dist/index.mjs
CHANGED
|
@@ -194,8 +194,8 @@ var require_mixdog_debug = __commonJS({
|
|
|
194
194
|
import React21 from "react";
|
|
195
195
|
import { render } from "../../../vendor/ink/build/index.js";
|
|
196
196
|
import { closeSync as closeSync2, constants as fsConstants, createWriteStream, mkdirSync as mkdirSync7, openSync as openSync2, readSync } from "node:fs";
|
|
197
|
-
import { tmpdir as
|
|
198
|
-
import { dirname as dirname9, join as
|
|
197
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
198
|
+
import { dirname as dirname9, join as join11 } from "node:path";
|
|
199
199
|
import { performance as performance3 } from "node:perf_hooks";
|
|
200
200
|
import { format } from "node:util";
|
|
201
201
|
|
|
@@ -4671,14 +4671,32 @@ function offsetAtCell(text, row, col, width) {
|
|
|
4671
4671
|
}
|
|
4672
4672
|
function boundaryPositions(text, width) {
|
|
4673
4673
|
const value = String(text || "");
|
|
4674
|
-
const
|
|
4675
|
-
|
|
4676
|
-
const
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4674
|
+
const w = safeWidth(width);
|
|
4675
|
+
const units = graphemeUnits(value);
|
|
4676
|
+
const positions = [{ offset: 0, row: 0, col: 0 }];
|
|
4677
|
+
let row = 0;
|
|
4678
|
+
let col = 0;
|
|
4679
|
+
for (let i = 0; i < units.length; i += 1) {
|
|
4680
|
+
const { end, segment } = units[i];
|
|
4681
|
+
if (segment === "\n") {
|
|
4682
|
+
row += 1;
|
|
4683
|
+
col = 0;
|
|
4684
|
+
} else {
|
|
4685
|
+
const segmentWidth = displayWidth(segment);
|
|
4686
|
+
if (segmentWidth > 0) {
|
|
4687
|
+
if (col > 0 && col + segmentWidth > w) {
|
|
4688
|
+
row += 1;
|
|
4689
|
+
col = 0;
|
|
4690
|
+
}
|
|
4691
|
+
col += segmentWidth;
|
|
4692
|
+
if (col === w && i < units.length - 1) {
|
|
4693
|
+
row += 1;
|
|
4694
|
+
col = 0;
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
positions.push({ offset: end, row, col });
|
|
4699
|
+
}
|
|
4682
4700
|
return positions;
|
|
4683
4701
|
}
|
|
4684
4702
|
function verticalOffset(text, offset, width, direction, preferredColumn = null) {
|
|
@@ -4919,11 +4937,14 @@ function PromptInput({
|
|
|
4919
4937
|
const commitDraft = (next, options = {}) => {
|
|
4920
4938
|
const sameDraft = draftStateEqual(draftRef.current, next);
|
|
4921
4939
|
if (!options.keepPreferredColumn) preferredColumnRef.current = null;
|
|
4922
|
-
if (sameDraft)
|
|
4940
|
+
if (sameDraft) {
|
|
4941
|
+
if (options.immediateSettle) scheduleImmediateFlush();
|
|
4942
|
+
return;
|
|
4943
|
+
}
|
|
4923
4944
|
if (!options.skipHistory) recordUndoSnapshot(draftRef.current, next, options);
|
|
4924
4945
|
draftRef.current = next;
|
|
4925
4946
|
setDraft(next);
|
|
4926
|
-
scheduleImmediateFlush();
|
|
4947
|
+
if (!options.throttledRender) scheduleImmediateFlush();
|
|
4927
4948
|
if (next.value !== lastReportedValueRef.current) {
|
|
4928
4949
|
lastReportedValueRef.current = next.value;
|
|
4929
4950
|
onDraftChange?.(next.value);
|
|
@@ -5024,13 +5045,13 @@ function PromptInput({
|
|
|
5024
5045
|
const queueMouseExtendCommit = (next, immediate = false) => {
|
|
5025
5046
|
if (immediate) {
|
|
5026
5047
|
cancelMouseExtendCoalesce();
|
|
5027
|
-
commitDraft(next);
|
|
5048
|
+
commitDraft(next, { immediateSettle: true });
|
|
5028
5049
|
mouseExtendCoalesceRef.current.t = Date.now();
|
|
5029
5050
|
return;
|
|
5030
5051
|
}
|
|
5031
5052
|
if (draftStateEqual(draftRef.current, next)) {
|
|
5032
5053
|
cancelMouseExtendCoalesce();
|
|
5033
|
-
commitDraft(next);
|
|
5054
|
+
commitDraft(next, { throttledRender: true });
|
|
5034
5055
|
return;
|
|
5035
5056
|
}
|
|
5036
5057
|
const state = mouseExtendCoalesceRef.current;
|
|
@@ -5040,7 +5061,7 @@ function PromptInput({
|
|
|
5040
5061
|
if (elapsed >= MOUSE_EXTEND_COALESCE_MS) {
|
|
5041
5062
|
cancelMouseExtendCoalesce();
|
|
5042
5063
|
state.t = now;
|
|
5043
|
-
commitDraft(next);
|
|
5064
|
+
commitDraft(next, { throttledRender: true });
|
|
5044
5065
|
return;
|
|
5045
5066
|
}
|
|
5046
5067
|
if (state.timer) return;
|
|
@@ -5050,7 +5071,7 @@ function PromptInput({
|
|
|
5050
5071
|
current.timer = null;
|
|
5051
5072
|
current.pendingNext = null;
|
|
5052
5073
|
current.t = Date.now();
|
|
5053
|
-
if (pending) commitDraft(pending);
|
|
5074
|
+
if (pending) commitDraft(pending, { throttledRender: true });
|
|
5054
5075
|
}, Math.max(1, MOUSE_EXTEND_COALESCE_MS - elapsed));
|
|
5055
5076
|
state.timer.unref?.();
|
|
5056
5077
|
};
|
|
@@ -6630,6 +6651,7 @@ import stringWidth6 from "string-width";
|
|
|
6630
6651
|
// src/tui/app/text-layout.mjs
|
|
6631
6652
|
import wrapAnsi from "wrap-ansi";
|
|
6632
6653
|
var graphemeSegmenter3 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
6654
|
+
var promptRowsCache = null;
|
|
6633
6655
|
function wrappedTextRows(value, width) {
|
|
6634
6656
|
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
6635
6657
|
let row = 0;
|
|
@@ -6657,7 +6679,14 @@ function wrappedTextRows(value, width) {
|
|
|
6657
6679
|
return row + 1;
|
|
6658
6680
|
}
|
|
6659
6681
|
function promptContentRows(value, contentColumns) {
|
|
6660
|
-
|
|
6682
|
+
const text = String(value ?? "");
|
|
6683
|
+
const width = Math.max(1, Math.floor(Number(contentColumns) || 1));
|
|
6684
|
+
if (promptRowsCache?.text === text && promptRowsCache.width === width) {
|
|
6685
|
+
return promptRowsCache.rows;
|
|
6686
|
+
}
|
|
6687
|
+
const rows = wrappedTextRows(`${text} `, width);
|
|
6688
|
+
promptRowsCache = { text, width, rows };
|
|
6689
|
+
return rows;
|
|
6661
6690
|
}
|
|
6662
6691
|
function wrappedDetailRows(text, width) {
|
|
6663
6692
|
const value = String(text ?? "").trim();
|
|
@@ -9539,6 +9568,18 @@ function resolveStreamingMarkdownParts(text, streamKey) {
|
|
|
9539
9568
|
}
|
|
9540
9569
|
|
|
9541
9570
|
// src/tui/markdown/measure-rendered-rows.mjs
|
|
9571
|
+
var streamingRowsByKey = /* @__PURE__ */ new Map();
|
|
9572
|
+
var STREAMING_ROWS_LRU_MAX = 32;
|
|
9573
|
+
function cacheStreamingRows(key, entry) {
|
|
9574
|
+
if (!key) return;
|
|
9575
|
+
if (streamingRowsByKey.has(key)) streamingRowsByKey.delete(key);
|
|
9576
|
+
streamingRowsByKey.set(key, entry);
|
|
9577
|
+
while (streamingRowsByKey.size > STREAMING_ROWS_LRU_MAX) {
|
|
9578
|
+
const oldest = streamingRowsByKey.keys().next().value;
|
|
9579
|
+
if (oldest === void 0) break;
|
|
9580
|
+
streamingRowsByKey.delete(oldest);
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9542
9583
|
function wrappedLineRows(line, width) {
|
|
9543
9584
|
const text = String(line);
|
|
9544
9585
|
const full = displayWidth(text);
|
|
@@ -9600,15 +9641,55 @@ function measureMarkdownRenderedRows(text, columns, { trimPartialFences = false
|
|
|
9600
9641
|
}
|
|
9601
9642
|
function measureStreamingMarkdownRenderedRows(text, columns, streamKey) {
|
|
9602
9643
|
const value = String(text ?? "");
|
|
9603
|
-
|
|
9644
|
+
const key = streamKey == null || streamKey === "" ? null : String(streamKey);
|
|
9645
|
+
const cached = key ? streamingRowsByKey.get(key) : null;
|
|
9646
|
+
if (cached && cached.text === value && cached.columns === columns) {
|
|
9647
|
+
cacheStreamingRows(key, cached);
|
|
9648
|
+
return cached.rows;
|
|
9649
|
+
}
|
|
9650
|
+
if (!value) {
|
|
9651
|
+
cacheStreamingRows(key, {
|
|
9652
|
+
text: value,
|
|
9653
|
+
columns,
|
|
9654
|
+
mode: "empty",
|
|
9655
|
+
rows: 1
|
|
9656
|
+
});
|
|
9657
|
+
return 1;
|
|
9658
|
+
}
|
|
9604
9659
|
const parts = resolveStreamingMarkdownParts(value, streamKey);
|
|
9605
9660
|
if (parts.plain) {
|
|
9606
|
-
|
|
9661
|
+
const plain = parts.unstableForRender;
|
|
9662
|
+
const width = Math.max(8, Number(columns || 80) - 3);
|
|
9663
|
+
const lastBreak = plain.lastIndexOf("\n");
|
|
9664
|
+
const stablePrefix = lastBreak >= 0 ? plain.substring(0, lastBreak + 1) : "";
|
|
9665
|
+
let stableRows2 = 0;
|
|
9666
|
+
if (cached && cached.mode === "plain" && cached.columns === columns && stablePrefix.startsWith(cached.stablePrefix)) {
|
|
9667
|
+
const addedComplete = plain.substring(cached.stablePrefix.length, Math.max(cached.stablePrefix.length, lastBreak));
|
|
9668
|
+
stableRows2 = cached.stableRows;
|
|
9669
|
+
if (addedComplete) {
|
|
9670
|
+
stableRows2 += addedComplete.split("\n").reduce((sum, line) => sum + wrappedLineRows(line, width), 0);
|
|
9671
|
+
}
|
|
9672
|
+
} else if (lastBreak >= 0) {
|
|
9673
|
+
stableRows2 = plain.substring(0, lastBreak).split("\n").reduce((sum, line) => sum + wrappedLineRows(line, width), 0);
|
|
9674
|
+
}
|
|
9675
|
+
const finalLine = lastBreak >= 0 ? plain.substring(lastBreak + 1) : plain;
|
|
9676
|
+
const rows2 = Math.max(1, stableRows2 + wrappedLineRows(finalLine, width));
|
|
9677
|
+
cacheStreamingRows(key, {
|
|
9678
|
+
text: value,
|
|
9679
|
+
columns,
|
|
9680
|
+
mode: "plain",
|
|
9681
|
+
stablePrefix,
|
|
9682
|
+
stableRows: stableRows2,
|
|
9683
|
+
rows: rows2
|
|
9684
|
+
});
|
|
9685
|
+
return rows2;
|
|
9607
9686
|
}
|
|
9608
9687
|
let rows = 0;
|
|
9609
9688
|
let childCount = 0;
|
|
9689
|
+
let stableRows = 0;
|
|
9610
9690
|
if (parts.stablePrefix) {
|
|
9611
|
-
|
|
9691
|
+
stableRows = cached && cached.mode === "markdown" && cached.columns === columns && cached.stablePrefix === parts.stablePrefix ? cached.stableRows : measureMarkdownRenderedRows(parts.stablePrefix, columns, { trimPartialFences: false });
|
|
9692
|
+
rows += stableRows;
|
|
9612
9693
|
childCount += 1;
|
|
9613
9694
|
}
|
|
9614
9695
|
if (parts.unstableSuffix) {
|
|
@@ -9616,8 +9697,16 @@ function measureStreamingMarkdownRenderedRows(text, columns, streamKey) {
|
|
|
9616
9697
|
childCount += 1;
|
|
9617
9698
|
}
|
|
9618
9699
|
if (childCount === 2) rows += 1;
|
|
9619
|
-
|
|
9620
|
-
|
|
9700
|
+
const measuredRows = childCount === 0 ? 1 : Math.max(1, rows);
|
|
9701
|
+
cacheStreamingRows(key, {
|
|
9702
|
+
text: value,
|
|
9703
|
+
columns,
|
|
9704
|
+
mode: "markdown",
|
|
9705
|
+
stablePrefix: parts.stablePrefix,
|
|
9706
|
+
stableRows,
|
|
9707
|
+
rows: measuredRows
|
|
9708
|
+
});
|
|
9709
|
+
return measuredRows;
|
|
9621
9710
|
}
|
|
9622
9711
|
|
|
9623
9712
|
// src/runtime/shared/err-text.mjs
|
|
@@ -10573,7 +10662,7 @@ function lowerBound(values, target) {
|
|
|
10573
10662
|
let hi = values.length;
|
|
10574
10663
|
while (lo < hi) {
|
|
10575
10664
|
const mid = Math.floor((lo + hi) / 2);
|
|
10576
|
-
if (values
|
|
10665
|
+
if (transcriptRowAt(values, mid) < target) lo = mid + 1;
|
|
10577
10666
|
else hi = mid;
|
|
10578
10667
|
}
|
|
10579
10668
|
return lo;
|
|
@@ -10583,14 +10672,14 @@ function upperBound(values, target) {
|
|
|
10583
10672
|
let hi = values.length;
|
|
10584
10673
|
while (lo < hi) {
|
|
10585
10674
|
const mid = Math.floor((lo + hi) / 2);
|
|
10586
|
-
if (values
|
|
10675
|
+
if (transcriptRowAt(values, mid) <= target) lo = mid + 1;
|
|
10587
10676
|
else hi = mid;
|
|
10588
10677
|
}
|
|
10589
10678
|
return lo;
|
|
10590
10679
|
}
|
|
10591
10680
|
function resolveAnchorScrollOffset({ anchor, items, curPrefix, totalRows, viewRows, maxRows }) {
|
|
10592
10681
|
if (!anchor || anchor.id == null) return null;
|
|
10593
|
-
if (!
|
|
10682
|
+
if (!curPrefix || curPrefix.length <= 1) return null;
|
|
10594
10683
|
const list = Array.isArray(items) ? items : [];
|
|
10595
10684
|
let idx = -1;
|
|
10596
10685
|
for (let i = list.length - 1; i >= 0; i--) {
|
|
@@ -10600,9 +10689,9 @@ function resolveAnchorScrollOffset({ anchor, items, curPrefix, totalRows, viewRo
|
|
|
10600
10689
|
}
|
|
10601
10690
|
}
|
|
10602
10691
|
if (idx < 0 || idx > curPrefix.length - 2) return null;
|
|
10603
|
-
const itemHeight = Math.max(0, (curPrefix
|
|
10692
|
+
const itemHeight = Math.max(0, transcriptRowAt(curPrefix, idx + 1) - transcriptRowAt(curPrefix, idx));
|
|
10604
10693
|
const clampedOffset = Math.max(0, Math.min(Number(anchor.offset) || 0, itemHeight));
|
|
10605
|
-
const anchorRowCur = (curPrefix
|
|
10694
|
+
const anchorRowCur = transcriptRowAt(curPrefix, idx) + clampedOffset;
|
|
10606
10695
|
return Math.max(0, Math.min(maxRows, totalRows - viewRows - anchorRowCur));
|
|
10607
10696
|
}
|
|
10608
10697
|
function fnv1a32(str) {
|
|
@@ -10662,8 +10751,21 @@ var transcriptRowsCache = /* @__PURE__ */ new WeakMap();
|
|
|
10662
10751
|
var transcriptMeasuredRowsCache = /* @__PURE__ */ new WeakMap();
|
|
10663
10752
|
var streamingMeasuredRowsById = /* @__PURE__ */ new Map();
|
|
10664
10753
|
var streamingEstimateHighWaterById = /* @__PURE__ */ new Map();
|
|
10754
|
+
var streamingTailEstimateById = /* @__PURE__ */ new Map();
|
|
10755
|
+
var STREAMING_TAIL_ESTIMATE_LRU_MAX = 8;
|
|
10756
|
+
function cacheStreamingTailEstimate(id, entry) {
|
|
10757
|
+
if (id == null) return;
|
|
10758
|
+
if (streamingTailEstimateById.has(id)) streamingTailEstimateById.delete(id);
|
|
10759
|
+
streamingTailEstimateById.set(id, entry);
|
|
10760
|
+
while (streamingTailEstimateById.size > STREAMING_TAIL_ESTIMATE_LRU_MAX) {
|
|
10761
|
+
const oldest = streamingTailEstimateById.keys().next().value;
|
|
10762
|
+
if (oldest === void 0) break;
|
|
10763
|
+
streamingTailEstimateById.delete(oldest);
|
|
10764
|
+
streamingEstimateHighWaterById.delete(oldest);
|
|
10765
|
+
}
|
|
10766
|
+
}
|
|
10665
10767
|
function hasStreamingRowStateToPrune() {
|
|
10666
|
-
return streamingMeasuredRowsById.size > 0 || streamingEstimateHighWaterById.size > 0;
|
|
10768
|
+
return streamingMeasuredRowsById.size > 0 || streamingEstimateHighWaterById.size > 0 || streamingTailEstimateById.size > 0;
|
|
10667
10769
|
}
|
|
10668
10770
|
function pruneStreamingMeasuredRowsById(liveIds) {
|
|
10669
10771
|
if (!liveIds) return;
|
|
@@ -10677,6 +10779,11 @@ function pruneStreamingMeasuredRowsById(liveIds) {
|
|
|
10677
10779
|
if (!liveIds.has(id)) streamingEstimateHighWaterById.delete(id);
|
|
10678
10780
|
}
|
|
10679
10781
|
}
|
|
10782
|
+
if (streamingTailEstimateById.size > 0) {
|
|
10783
|
+
for (const id of streamingTailEstimateById.keys()) {
|
|
10784
|
+
if (!liveIds.has(id)) streamingTailEstimateById.delete(id);
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10680
10787
|
}
|
|
10681
10788
|
function carryTranscriptMeasuredRowsCache(prevItem, nextItem) {
|
|
10682
10789
|
if (!TRANSCRIPT_MEASURED_ROWS || !prevItem || !nextItem || prevItem === nextItem) return;
|
|
@@ -10707,20 +10814,42 @@ function assistantTextForStreamingRowEstimate(text) {
|
|
|
10707
10814
|
return streamingLayoutText(text);
|
|
10708
10815
|
}
|
|
10709
10816
|
function streamingEstimateRows(item, columns, toolOutputExpanded) {
|
|
10817
|
+
const id = item?.id;
|
|
10818
|
+
const exactText = String(item?.text ?? "");
|
|
10819
|
+
const toolExpanded = toolOutputExpanded ? 1 : 0;
|
|
10820
|
+
const renderMode = item?.kind === "assistant" && item?.streaming ? "assistant-streaming-markdown" : "other";
|
|
10821
|
+
const cached = id == null ? null : streamingTailEstimateById.get(id);
|
|
10822
|
+
if (cached && cached.text === exactText && cached.columns === columns && cached.toolExpanded === toolExpanded && cached.renderMode === renderMode) {
|
|
10823
|
+
cacheStreamingTailEstimate(id, cached);
|
|
10824
|
+
return cached.rows;
|
|
10825
|
+
}
|
|
10710
10826
|
const trimmedText = assistantTextForStreamingRowEstimate(item.text);
|
|
10711
10827
|
const estimateItem = trimmedText === item.text ? item : { ...item, text: trimmedText };
|
|
10712
10828
|
const raw = Math.max(1, Math.ceil(estimateTranscriptItemRows(estimateItem, columns, toolOutputExpanded)));
|
|
10713
10829
|
const quantized = Math.ceil(raw / STREAMING_ROW_QUANTUM) * STREAMING_ROW_QUANTUM;
|
|
10714
|
-
const id = item?.id;
|
|
10715
10830
|
if (id == null) return quantized;
|
|
10716
|
-
const toolExpanded = toolOutputExpanded ? 1 : 0;
|
|
10717
10831
|
const prev = streamingEstimateHighWaterById.get(id);
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10832
|
+
let rows;
|
|
10833
|
+
if (!prev || prev.columns !== columns || prev.toolExpanded !== toolExpanded || prev.renderMode !== renderMode) {
|
|
10834
|
+
streamingEstimateHighWaterById.set(id, {
|
|
10835
|
+
rows: quantized,
|
|
10836
|
+
columns,
|
|
10837
|
+
toolExpanded,
|
|
10838
|
+
renderMode
|
|
10839
|
+
});
|
|
10840
|
+
rows = quantized;
|
|
10841
|
+
} else {
|
|
10842
|
+
if (quantized > prev.rows) prev.rows = quantized;
|
|
10843
|
+
rows = prev.rows;
|
|
10721
10844
|
}
|
|
10722
|
-
|
|
10723
|
-
|
|
10845
|
+
cacheStreamingTailEstimate(id, {
|
|
10846
|
+
text: exactText,
|
|
10847
|
+
columns,
|
|
10848
|
+
toolExpanded,
|
|
10849
|
+
renderMode,
|
|
10850
|
+
rows
|
|
10851
|
+
});
|
|
10852
|
+
return rows;
|
|
10724
10853
|
}
|
|
10725
10854
|
function streamingTailMountedGrowth(items, columns, toolOutputExpanded) {
|
|
10726
10855
|
const list = Array.isArray(items) ? items : [];
|
|
@@ -10752,12 +10881,14 @@ function estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded) {
|
|
|
10752
10881
|
if (idEntry) {
|
|
10753
10882
|
streamingMeasuredRowsById.delete(item.id);
|
|
10754
10883
|
streamingEstimateHighWaterById.delete(item.id);
|
|
10884
|
+
streamingTailEstimateById.delete(item.id);
|
|
10755
10885
|
}
|
|
10756
10886
|
return streamingEstimateRows(item, columns, toolOutputExpanded);
|
|
10757
10887
|
}
|
|
10758
10888
|
if (item.kind === "assistant") {
|
|
10759
10889
|
if (streamingMeasuredRowsById.has(item.id)) streamingMeasuredRowsById.delete(item.id);
|
|
10760
10890
|
if (streamingEstimateHighWaterById.has(item.id)) streamingEstimateHighWaterById.delete(item.id);
|
|
10891
|
+
if (streamingTailEstimateById.has(item.id)) streamingTailEstimateById.delete(item.id);
|
|
10761
10892
|
}
|
|
10762
10893
|
const variantKey = transcriptItemVariantKey(item);
|
|
10763
10894
|
const toolExpanded = toolOutputExpanded ? 1 : 0;
|
|
@@ -10772,14 +10903,15 @@ function estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded) {
|
|
|
10772
10903
|
function buildTranscriptRowIndex(items, {
|
|
10773
10904
|
columns = 80,
|
|
10774
10905
|
toolOutputExpanded = false,
|
|
10775
|
-
suppressMeasuredRowHeights = false
|
|
10906
|
+
suppressMeasuredRowHeights = false,
|
|
10907
|
+
streamingTailItem = null
|
|
10776
10908
|
} = {}) {
|
|
10777
10909
|
const allItems = Array.isArray(items) ? items : [];
|
|
10778
10910
|
const rows = new Array(allItems.length);
|
|
10779
10911
|
const prefixRows = new Array(allItems.length + 1);
|
|
10780
10912
|
prefixRows[0] = 0;
|
|
10781
10913
|
for (let i = 0; i < allItems.length; i++) {
|
|
10782
|
-
const item = allItems[i];
|
|
10914
|
+
const item = streamingTailItem && i === allItems.length - 1 ? streamingTailItem : allItems[i];
|
|
10783
10915
|
const measured = suppressMeasuredRowHeights ? null : measuredTranscriptRows(item, columns, toolOutputExpanded);
|
|
10784
10916
|
const rowCount = measured != null ? measured : estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded);
|
|
10785
10917
|
rows[i] = rowCount;
|
|
@@ -10797,17 +10929,19 @@ function buildTranscriptRowIndexIncremental(items, {
|
|
|
10797
10929
|
suppressMeasuredRowHeights = false,
|
|
10798
10930
|
measuredRowsVersion = 0,
|
|
10799
10931
|
cacheRef = null,
|
|
10800
|
-
prefixRevision = null
|
|
10932
|
+
prefixRevision = null,
|
|
10933
|
+
streamingTailItem = null
|
|
10801
10934
|
} = {}) {
|
|
10802
10935
|
const allItems = Array.isArray(items) ? items : [];
|
|
10803
|
-
const tail = trailingStreamingItem(allItems);
|
|
10936
|
+
const tail = streamingTailItem || trailingStreamingItem(allItems);
|
|
10804
10937
|
const holder = cacheRef || { current: null };
|
|
10805
10938
|
if (!tail) {
|
|
10806
10939
|
holder.current = null;
|
|
10807
10940
|
return buildTranscriptRowIndex(allItems, {
|
|
10808
10941
|
columns,
|
|
10809
10942
|
toolOutputExpanded,
|
|
10810
|
-
suppressMeasuredRowHeights
|
|
10943
|
+
suppressMeasuredRowHeights,
|
|
10944
|
+
streamingTailItem
|
|
10811
10945
|
});
|
|
10812
10946
|
}
|
|
10813
10947
|
const prefixLen = allItems.length - 1;
|
|
@@ -10816,17 +10950,19 @@ function buildTranscriptRowIndexIncremental(items, {
|
|
|
10816
10950
|
{
|
|
10817
10951
|
const tailMeasured = suppressMeasuredRowHeights ? null : measuredTranscriptRows(tail, columns, toolOutputExpanded);
|
|
10818
10952
|
const tailRows = tailMeasured != null ? tailMeasured : estimateTranscriptItemRowsCached(tail, columns, toolOutputExpanded);
|
|
10819
|
-
const
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10953
|
+
const totalRows = cache.prefixTotal + tailRows;
|
|
10954
|
+
return {
|
|
10955
|
+
rows: appendTranscriptRow(cache.prefixRowsArr, tailRows),
|
|
10956
|
+
prefixRows: appendTranscriptRow(cache.prefixPrefixRows, totalRows),
|
|
10957
|
+
totalRows
|
|
10958
|
+
};
|
|
10824
10959
|
}
|
|
10825
10960
|
}
|
|
10826
10961
|
const full = buildTranscriptRowIndex(allItems, {
|
|
10827
10962
|
columns,
|
|
10828
10963
|
toolOutputExpanded,
|
|
10829
|
-
suppressMeasuredRowHeights
|
|
10964
|
+
suppressMeasuredRowHeights,
|
|
10965
|
+
streamingTailItem
|
|
10830
10966
|
});
|
|
10831
10967
|
holder.current = {
|
|
10832
10968
|
columns,
|
|
@@ -10842,6 +10978,42 @@ function buildTranscriptRowIndexIncremental(items, {
|
|
|
10842
10978
|
};
|
|
10843
10979
|
return full;
|
|
10844
10980
|
}
|
|
10981
|
+
function transcriptStructureSignature(items, columns, toolOutputExpanded, prefixRevision = 0, streamingTailItem = null) {
|
|
10982
|
+
const list = Array.isArray(items) ? items : [];
|
|
10983
|
+
const tail = streamingTailItem || trailingStreamingItem(list);
|
|
10984
|
+
const tailRows = tail ? estimateTranscriptItemRowsCached(tail, columns, toolOutputExpanded) : 0;
|
|
10985
|
+
return `${Math.max(0, Number(prefixRevision) || 0)}|${list.length}|${columns}|${toolOutputExpanded ? 1 : 0}|${tail?.id ?? "_"}:${tailRows}`;
|
|
10986
|
+
}
|
|
10987
|
+
function transcriptRowAt(values, index) {
|
|
10988
|
+
if (!values || index < 0 || index >= values.length) return 0;
|
|
10989
|
+
return typeof values.atIndex === "function" ? values.atIndex(index) : Number(values[index]) || 0;
|
|
10990
|
+
}
|
|
10991
|
+
function appendTranscriptRow(prefix, tailValue) {
|
|
10992
|
+
return Object.freeze({
|
|
10993
|
+
length: prefix.length + 1,
|
|
10994
|
+
atIndex: (index) => index === prefix.length ? tailValue : Number(prefix[index]) || 0,
|
|
10995
|
+
slice: (start = 0, end = prefix.length + 1) => {
|
|
10996
|
+
const values = [];
|
|
10997
|
+
const lo = Math.max(0, start < 0 ? prefix.length + 1 + start : start);
|
|
10998
|
+
const hi = Math.min(prefix.length + 1, end < 0 ? prefix.length + 1 + end : end);
|
|
10999
|
+
for (let index = lo; index < hi; index++) {
|
|
11000
|
+
values.push(index === prefix.length ? tailValue : prefix[index]);
|
|
11001
|
+
}
|
|
11002
|
+
return values;
|
|
11003
|
+
}
|
|
11004
|
+
});
|
|
11005
|
+
}
|
|
11006
|
+
function transcriptItemsWithStableTail(settledItems, streamingTailItem, cacheRef) {
|
|
11007
|
+
const settled = Array.isArray(settledItems) ? settledItems : [];
|
|
11008
|
+
if (!streamingTailItem) return settled;
|
|
11009
|
+
const previous = cacheRef?.current;
|
|
11010
|
+
if (previous && previous.settled === settled && previous.tailId === streamingTailItem.id) {
|
|
11011
|
+
return previous.items;
|
|
11012
|
+
}
|
|
11013
|
+
const items = [...settled, streamingTailItem];
|
|
11014
|
+
if (cacheRef) cacheRef.current = { settled, tailId: streamingTailItem.id, items };
|
|
11015
|
+
return items;
|
|
11016
|
+
}
|
|
10845
11017
|
function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24, columns = 80, toolOutputExpanded = false, rowIndex = null } = {}) {
|
|
10846
11018
|
const allItems = Array.isArray(items) ? items : [];
|
|
10847
11019
|
const itemCount = allItems.length;
|
|
@@ -10878,7 +11050,7 @@ function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24,
|
|
|
10878
11050
|
endIndex = Math.min(itemCount, Math.max(visibleEndIndex, startIndex + effectiveMaxItems));
|
|
10879
11051
|
if (endIndex - startIndex > effectiveMaxItems) startIndex = Math.max(0, endIndex - effectiveMaxItems);
|
|
10880
11052
|
}
|
|
10881
|
-
const bottomSpacerRows = Math.max(0, totalRows - (prefixRows
|
|
11053
|
+
const bottomSpacerRows = Math.max(0, totalRows - (transcriptRowAt(prefixRows, endIndex) || totalRows));
|
|
10882
11054
|
return {
|
|
10883
11055
|
startIndex,
|
|
10884
11056
|
endIndex,
|
|
@@ -11016,6 +11188,9 @@ function useMouseInput({
|
|
|
11016
11188
|
promptMouseSelectionRef.current?.clear?.();
|
|
11017
11189
|
applySelectionRect(null);
|
|
11018
11190
|
};
|
|
11191
|
+
const finishWindowsMouseGesture = () => {
|
|
11192
|
+
if (IS_WINDOWS_TERMINAL) store.forceRenderRepaint?.();
|
|
11193
|
+
};
|
|
11019
11194
|
const EDGE_AUTOSCROLL_INTERVAL_MS = 50;
|
|
11020
11195
|
const stopEdgeAutoscroll = () => {
|
|
11021
11196
|
const st = edgeAutoscrollRef.current;
|
|
@@ -11089,6 +11264,7 @@ function useMouseInput({
|
|
|
11089
11264
|
if (dragRef.current.active) {
|
|
11090
11265
|
const last = dragRef.current.last || {};
|
|
11091
11266
|
finalizeActiveDrag(Number(last.x) || 0, Number(last.y) || 0);
|
|
11267
|
+
finishWindowsMouseGesture();
|
|
11092
11268
|
} else {
|
|
11093
11269
|
stopEdgeAutoscroll();
|
|
11094
11270
|
}
|
|
@@ -11123,9 +11299,6 @@ function useMouseInput({
|
|
|
11123
11299
|
const shiftHeld = (button & MOUSE_SHIFT_MASK) !== 0;
|
|
11124
11300
|
const ctrlHeld = (button & MOUSE_CTRL_MASK) !== 0;
|
|
11125
11301
|
if (IS_WINDOWS_TERMINAL && shiftHeld) return;
|
|
11126
|
-
if (IS_WINDOWS_TERMINAL && press && !isMotion) {
|
|
11127
|
-
store.forceRenderRepaint?.();
|
|
11128
|
-
}
|
|
11129
11302
|
const extendHeld = shiftHeld || ctrlHeld;
|
|
11130
11303
|
const isRightPress = baseButton === 2 && press && !isMotion;
|
|
11131
11304
|
if (isRightPress) {
|
|
@@ -11138,6 +11311,7 @@ function useMouseInput({
|
|
|
11138
11311
|
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: false, rect: null, region: "prompt", anchorSpan: null };
|
|
11139
11312
|
if (offset != null) ctl.extendTo?.(offset, true);
|
|
11140
11313
|
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
11314
|
+
finishWindowsMouseGesture();
|
|
11141
11315
|
}
|
|
11142
11316
|
return;
|
|
11143
11317
|
}
|
|
@@ -11155,6 +11329,7 @@ function useMouseInput({
|
|
|
11155
11329
|
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
11156
11330
|
applySelectionRect(rect);
|
|
11157
11331
|
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
11332
|
+
finishWindowsMouseGesture();
|
|
11158
11333
|
return;
|
|
11159
11334
|
}
|
|
11160
11335
|
if (dragRef.current.region === regionR && !dragRef.current.anchorSpan && dragRef.current.anchor && dragRef.current.rect && !(dragRef.current.rect.x1 === dragRef.current.rect.x2 && dragRef.current.rect.y1 === dragRef.current.rect.y2)) {
|
|
@@ -11166,6 +11341,7 @@ function useMouseInput({
|
|
|
11166
11341
|
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
11167
11342
|
applySelectionRect(rect);
|
|
11168
11343
|
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
11344
|
+
finishWindowsMouseGesture();
|
|
11169
11345
|
return;
|
|
11170
11346
|
}
|
|
11171
11347
|
return;
|
|
@@ -11208,6 +11384,7 @@ function useMouseInput({
|
|
|
11208
11384
|
dragRef.current.region = null;
|
|
11209
11385
|
dragRef.current.anchorSpan = null;
|
|
11210
11386
|
clearAllSelections();
|
|
11387
|
+
finishWindowsMouseGesture();
|
|
11211
11388
|
return;
|
|
11212
11389
|
}
|
|
11213
11390
|
const region = inTranscript ? "transcript" : "status";
|
|
@@ -11317,6 +11494,7 @@ function useMouseInput({
|
|
|
11317
11494
|
}
|
|
11318
11495
|
} else if (!press && baseButton === 0 && dragRef.current.active) {
|
|
11319
11496
|
finalizeActiveDrag(x, y);
|
|
11497
|
+
finishWindowsMouseGesture();
|
|
11320
11498
|
}
|
|
11321
11499
|
}
|
|
11322
11500
|
};
|
|
@@ -11330,6 +11508,73 @@ function useMouseInput({
|
|
|
11330
11508
|
|
|
11331
11509
|
// src/tui/app/use-transcript-scroll.mjs
|
|
11332
11510
|
import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
11511
|
+
|
|
11512
|
+
// src/tui/engine/render-timing.mjs
|
|
11513
|
+
var RENDER_ACK_HANG_GUARD_MS = 250;
|
|
11514
|
+
var RENDER_SETTLE_IDLE_MS = 64;
|
|
11515
|
+
var pendingRenderAcks = [];
|
|
11516
|
+
var renderAckSeq = 0;
|
|
11517
|
+
var scheduleRenderFrameAck = () => {
|
|
11518
|
+
const seq = ++renderAckSeq;
|
|
11519
|
+
setImmediate(() => notifyRenderFrame(seq));
|
|
11520
|
+
};
|
|
11521
|
+
var notifyRenderFrame = (seq = ++renderAckSeq) => {
|
|
11522
|
+
if (pendingRenderAcks.length === 0) return;
|
|
11523
|
+
const acks = pendingRenderAcks;
|
|
11524
|
+
pendingRenderAcks = [];
|
|
11525
|
+
for (const ack of acks) ack(seq);
|
|
11526
|
+
};
|
|
11527
|
+
var yieldToRenderer = ({ frames = 1 } = {}) => new Promise((resolve5) => {
|
|
11528
|
+
const minSeq = renderAckSeq;
|
|
11529
|
+
let remainingFrames = Math.max(1, Math.floor(Number(frames) || 1));
|
|
11530
|
+
let settled = false;
|
|
11531
|
+
let sawRealFrame = false;
|
|
11532
|
+
let timer2 = null;
|
|
11533
|
+
const finish = () => {
|
|
11534
|
+
if (settled) return;
|
|
11535
|
+
settled = true;
|
|
11536
|
+
if (timer2) clearTimeout(timer2);
|
|
11537
|
+
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
11538
|
+
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
11539
|
+
resolve5();
|
|
11540
|
+
};
|
|
11541
|
+
const onTimeout = () => {
|
|
11542
|
+
if (settled) return;
|
|
11543
|
+
if (!sawRealFrame && renderAckSeq > minSeq) {
|
|
11544
|
+
setImmediate(() => {
|
|
11545
|
+
if (!settled && !sawRealFrame) finish();
|
|
11546
|
+
});
|
|
11547
|
+
return;
|
|
11548
|
+
}
|
|
11549
|
+
finish();
|
|
11550
|
+
};
|
|
11551
|
+
const armWait = () => {
|
|
11552
|
+
if (settled) return;
|
|
11553
|
+
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
11554
|
+
if (timer2) clearTimeout(timer2);
|
|
11555
|
+
timer2 = setTimeout(onTimeout, sawRealFrame ? RENDER_SETTLE_IDLE_MS : RENDER_ACK_HANG_GUARD_MS);
|
|
11556
|
+
};
|
|
11557
|
+
const onFrame = (seq = 0) => {
|
|
11558
|
+
if (settled) return;
|
|
11559
|
+
if (seq <= minSeq) {
|
|
11560
|
+
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
11561
|
+
return;
|
|
11562
|
+
}
|
|
11563
|
+
sawRealFrame = true;
|
|
11564
|
+
if (timer2) {
|
|
11565
|
+
clearTimeout(timer2);
|
|
11566
|
+
timer2 = null;
|
|
11567
|
+
}
|
|
11568
|
+
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
11569
|
+
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
11570
|
+
remainingFrames -= 1;
|
|
11571
|
+
if (remainingFrames <= 0) finish();
|
|
11572
|
+
else armWait();
|
|
11573
|
+
};
|
|
11574
|
+
armWait();
|
|
11575
|
+
});
|
|
11576
|
+
|
|
11577
|
+
// src/tui/app/use-transcript-scroll.mjs
|
|
11333
11578
|
function useTranscriptScroll({
|
|
11334
11579
|
store,
|
|
11335
11580
|
frameColumns,
|
|
@@ -11352,7 +11597,7 @@ function useTranscriptScroll({
|
|
|
11352
11597
|
const scrollAnimationRef = useRef8(null);
|
|
11353
11598
|
const selectionPaintRef = useRef8({ t: 0, rect: null, pending: null, timer: null });
|
|
11354
11599
|
const scrollCoalesceRef = useRef8({ pendingRows: 0, timer: null });
|
|
11355
|
-
const
|
|
11600
|
+
const selectionTextCaptureRef = useRef8(0);
|
|
11356
11601
|
const stitchBufferRef = useRef8(/* @__PURE__ */ new Map());
|
|
11357
11602
|
const stitchHarvestTimerRef = useRef8(null);
|
|
11358
11603
|
const stitchHarvestScrollRef = useRef8(0);
|
|
@@ -11465,12 +11710,12 @@ function useTranscriptScroll({
|
|
|
11465
11710
|
stopSmoothScroll();
|
|
11466
11711
|
}, [stopSmoothScroll]);
|
|
11467
11712
|
const rememberSelectionTextSoon = useCallback3(() => {
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11713
|
+
const capture = ++selectionTextCaptureRef.current;
|
|
11714
|
+
void yieldToRenderer().then(() => {
|
|
11715
|
+
if (capture !== selectionTextCaptureRef.current) return;
|
|
11471
11716
|
const text = store.getRenderSelectionText?.();
|
|
11472
11717
|
if (text && text.trim()) selectionTextRef.current = text;
|
|
11473
|
-
}
|
|
11718
|
+
});
|
|
11474
11719
|
}, [store]);
|
|
11475
11720
|
const selectionClip = useCallback3(() => {
|
|
11476
11721
|
if (dragRef.current.region === "status") {
|
|
@@ -11496,22 +11741,20 @@ function useTranscriptScroll({
|
|
|
11496
11741
|
if (options.captureText === false) clipped.captureText = false;
|
|
11497
11742
|
return clipped;
|
|
11498
11743
|
}, [selectionClip]);
|
|
11499
|
-
const paintSelectionRect = useCallback3((clippedRect, { rememberText = true
|
|
11744
|
+
const paintSelectionRect = useCallback3((clippedRect, { rememberText = true } = {}) => {
|
|
11500
11745
|
const nextRect = clippedRect || null;
|
|
11501
11746
|
const state = selectionPaintRef.current;
|
|
11502
11747
|
if (selectionRectsEqual(state.rect, nextRect)) {
|
|
11503
11748
|
const needsCapture = nextRect && rememberText && nextRect.captureText !== false;
|
|
11504
|
-
if (!
|
|
11505
|
-
|
|
11506
|
-
store.setRenderSelection?.(nextRect, { immediate: true });
|
|
11507
|
-
}
|
|
11749
|
+
if (!needsCapture) return false;
|
|
11750
|
+
store.setRenderSelection?.(nextRect);
|
|
11508
11751
|
if (needsCapture) rememberSelectionTextSoon();
|
|
11509
11752
|
if (nextRect) harvestStitchRowsSoon();
|
|
11510
11753
|
return true;
|
|
11511
11754
|
}
|
|
11512
11755
|
state.rect = nextRect;
|
|
11513
11756
|
state.t = Date.now();
|
|
11514
|
-
store.setRenderSelection?.(nextRect
|
|
11757
|
+
store.setRenderSelection?.(nextRect);
|
|
11515
11758
|
if (nextRect && rememberText && nextRect.captureText !== false) rememberSelectionTextSoon();
|
|
11516
11759
|
if (nextRect) harvestStitchRowsSoon();
|
|
11517
11760
|
return true;
|
|
@@ -11533,17 +11776,18 @@ function useTranscriptScroll({
|
|
|
11533
11776
|
state.timer = null;
|
|
11534
11777
|
}
|
|
11535
11778
|
state.pending = null;
|
|
11536
|
-
if (pending) paintSelectionRect(pending, { rememberText: false
|
|
11779
|
+
if (pending) paintSelectionRect(pending, { rememberText: false });
|
|
11537
11780
|
}, [paintSelectionRect]);
|
|
11538
11781
|
const applySelectionRect = useCallback3((rect) => {
|
|
11539
11782
|
const clippedRect = withSelectionClip(rect);
|
|
11540
11783
|
dragRef.current.rect = clippedRect || null;
|
|
11541
11784
|
if (!clippedRect) {
|
|
11785
|
+
selectionTextCaptureRef.current += 1;
|
|
11542
11786
|
selectionTextRef.current = "";
|
|
11543
11787
|
clearStitchBuffer();
|
|
11544
11788
|
}
|
|
11545
11789
|
cancelPendingSelectionPaint();
|
|
11546
|
-
paintSelectionRect(clippedRect, { rememberText: true
|
|
11790
|
+
paintSelectionRect(clippedRect, { rememberText: true });
|
|
11547
11791
|
}, [paintSelectionRect, withSelectionClip, clearStitchBuffer, cancelPendingSelectionPaint]);
|
|
11548
11792
|
const applySelectionRectThrottled = useCallback3((rect) => {
|
|
11549
11793
|
const clippedRect = withSelectionClip(rect, { captureText: false });
|
|
@@ -11634,8 +11878,7 @@ function useTranscriptScroll({
|
|
|
11634
11878
|
if (paintState.timer) clearTimeout(paintState.timer);
|
|
11635
11879
|
paintState.timer = null;
|
|
11636
11880
|
paintState.pending = null;
|
|
11637
|
-
|
|
11638
|
-
selectionTextTimerRef.current = null;
|
|
11881
|
+
selectionTextCaptureRef.current += 1;
|
|
11639
11882
|
if (stitchHarvestTimerRef.current) clearTimeout(stitchHarvestTimerRef.current);
|
|
11640
11883
|
stitchHarvestTimerRef.current = null;
|
|
11641
11884
|
const coalesceState = scrollCoalesceRef.current;
|
|
@@ -11645,6 +11888,44 @@ function useTranscriptScroll({
|
|
|
11645
11888
|
}, []);
|
|
11646
11889
|
const scrollTranscriptRows = useCallback3((deltaRows, options = {}) => {
|
|
11647
11890
|
const maxTarget = Math.max(0, Number(maxScrollRowsRef.current) || 0);
|
|
11891
|
+
const historyState = store.getState?.() || {};
|
|
11892
|
+
const captureRestoreAnchor = () => {
|
|
11893
|
+
const geom = transcriptGeomRef.current || {};
|
|
11894
|
+
const prefixRows = geom.prefixRows;
|
|
11895
|
+
if (!prefixRows || prefixRows.length <= 1) return;
|
|
11896
|
+
const total = Math.max(0, Number(geom.totalRows) || 0);
|
|
11897
|
+
const view = Math.max(1, Number(geom.viewRows) || 1);
|
|
11898
|
+
const target2 = Math.max(0, Number(scrollTargetRef.current) || 0);
|
|
11899
|
+
const row = Math.max(0, Math.min(total, total - target2 - view));
|
|
11900
|
+
let index = upperBound(prefixRows, row) - 1;
|
|
11901
|
+
index = Math.max(0, Math.min(prefixRows.length - 2, index));
|
|
11902
|
+
const item = geom.items?.[index];
|
|
11903
|
+
if (item?.id != null) {
|
|
11904
|
+
transcriptAnchorRef.current = {
|
|
11905
|
+
id: item.id,
|
|
11906
|
+
offset: Math.max(0, row - transcriptRowAt(prefixRows, index))
|
|
11907
|
+
};
|
|
11908
|
+
transcriptAnchorDirtyRef.current = false;
|
|
11909
|
+
}
|
|
11910
|
+
};
|
|
11911
|
+
if (deltaRows > 0 && scrollTargetRef.current >= maxTarget && historyState.transcriptHistoryBefore) {
|
|
11912
|
+
captureRestoreAnchor();
|
|
11913
|
+
if (!store.restoreOlderTranscript?.()) return;
|
|
11914
|
+
stopSmoothScroll();
|
|
11915
|
+
cancelTranscriptFollow();
|
|
11916
|
+
return;
|
|
11917
|
+
}
|
|
11918
|
+
if (deltaRows < 0 && scrollTargetRef.current <= 0 && historyState.transcriptHistoryAfter) {
|
|
11919
|
+
captureRestoreAnchor();
|
|
11920
|
+
if (!store.restoreNewerTranscript?.()) return;
|
|
11921
|
+
stopSmoothScroll();
|
|
11922
|
+
cancelTranscriptFollow();
|
|
11923
|
+
const anchoredTarget = Math.max(1, maxTarget);
|
|
11924
|
+
scrollTargetRef.current = anchoredTarget;
|
|
11925
|
+
scrollPositionRef.current = anchoredTarget;
|
|
11926
|
+
setScrollOffset(anchoredTarget);
|
|
11927
|
+
return;
|
|
11928
|
+
}
|
|
11648
11929
|
const target = Math.max(0, Math.min(maxTarget, scrollTargetRef.current + deltaRows));
|
|
11649
11930
|
const appliedDelta = target - scrollTargetRef.current;
|
|
11650
11931
|
if (appliedDelta !== 0 && dragRef.current.region === "transcript" && dragRef.current.rect) {
|
|
@@ -11660,7 +11941,7 @@ function useTranscriptScroll({
|
|
|
11660
11941
|
} else {
|
|
11661
11942
|
const geom = transcriptGeomRef.current || {};
|
|
11662
11943
|
const prefixRows = geom.prefixRows;
|
|
11663
|
-
if (
|
|
11944
|
+
if (prefixRows && prefixRows.length > 1) {
|
|
11664
11945
|
const gTotal = Math.max(0, Number(geom.totalRows) || 0);
|
|
11665
11946
|
const gView = Math.max(1, Number(geom.viewRows) || 1);
|
|
11666
11947
|
const anchorRow = Math.max(0, Math.min(gTotal, gTotal - target - gView));
|
|
@@ -11670,7 +11951,7 @@ function useTranscriptScroll({
|
|
|
11670
11951
|
const items = geom.items || [];
|
|
11671
11952
|
const anchorItem = items[idx];
|
|
11672
11953
|
if (anchorItem && anchorItem.id != null) {
|
|
11673
|
-
transcriptAnchorRef.current = { id: anchorItem.id, offset: Math.max(0, anchorRow - (prefixRows
|
|
11954
|
+
transcriptAnchorRef.current = { id: anchorItem.id, offset: Math.max(0, anchorRow - transcriptRowAt(prefixRows, idx)) };
|
|
11674
11955
|
transcriptAnchorDirtyRef.current = false;
|
|
11675
11956
|
} else {
|
|
11676
11957
|
transcriptAnchorDirtyRef.current = true;
|
|
@@ -11867,6 +12148,7 @@ function useTranscriptWindow({
|
|
|
11867
12148
|
const transcriptTotalRowsRef = useRef9(0);
|
|
11868
12149
|
const committedMaxScrollRowsRef = useRef9(0);
|
|
11869
12150
|
const incrementalRowIndexCacheRef = useRef9(null);
|
|
12151
|
+
const transcriptItemsCacheRef = useRef9(null);
|
|
11870
12152
|
const prevViewportGeomRef = useRef9({ contentHeight: 0, floatingPanelRows: 0 });
|
|
11871
12153
|
const transcriptItemElsRef = useRef9(/* @__PURE__ */ new Map());
|
|
11872
12154
|
const transcriptMeasureRefCache = useRef9(/* @__PURE__ */ new Map());
|
|
@@ -11901,12 +12183,18 @@ function useTranscriptWindow({
|
|
|
11901
12183
|
const tailRows = streamingTailItem ? estimateTranscriptItemRowsCached(streamingTailItem, frameColumns, toolOutputExpanded) : 0;
|
|
11902
12184
|
const tailSig = streamingTailItem ? `${streamingTailItem.id}:${tailRows}` : "_";
|
|
11903
12185
|
const revision = Math.max(0, Number(structureRevision) || 0);
|
|
11904
|
-
const transcriptItems =
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
12186
|
+
const transcriptItems = transcriptItemsWithStableTail(
|
|
12187
|
+
settledItems,
|
|
12188
|
+
streamingTailItem,
|
|
12189
|
+
transcriptItemsCacheRef
|
|
12190
|
+
);
|
|
12191
|
+
const transcriptStructureSig = transcriptStructureSignature(
|
|
12192
|
+
transcriptItems,
|
|
12193
|
+
frameColumns,
|
|
12194
|
+
toolOutputExpanded,
|
|
12195
|
+
revision,
|
|
12196
|
+
streamingTailItem
|
|
11908
12197
|
);
|
|
11909
|
-
const transcriptStructureSig = `${revision}#${tailSig}`;
|
|
11910
12198
|
const transcriptStreamingActive = !!streamingTailItem;
|
|
11911
12199
|
const scrolledUpForStreamingMeasure = scrolledUpRowsForPin > 0;
|
|
11912
12200
|
const prevEstimateGeometry = transcriptGeomRef.current?.suppressMeasuredRowHeights === true;
|
|
@@ -11919,7 +12207,8 @@ function useTranscriptWindow({
|
|
|
11919
12207
|
suppressMeasuredRowHeights,
|
|
11920
12208
|
measuredRowsVersion,
|
|
11921
12209
|
cacheRef: incrementalRowIndexCacheRef,
|
|
11922
|
-
prefixRevision: revision
|
|
12210
|
+
prefixRevision: revision,
|
|
12211
|
+
streamingTailItem
|
|
11923
12212
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- revision/tail height capture structural geometry; measuredRowsVersion folds in measured corrections
|
|
11924
12213
|
}), [revision, tailSig, frameColumns, toolOutputExpanded, measuredRowsVersion, suppressMeasuredRowHeights]);
|
|
11925
12214
|
const hasReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current;
|
|
@@ -11946,7 +12235,7 @@ function useTranscriptWindow({
|
|
|
11946
12235
|
} else if (!followingRef.current && !nearBottomWithoutAnchor && scrolledUp) {
|
|
11947
12236
|
const geom = transcriptGeomRef.current || {};
|
|
11948
12237
|
const prevPrefix = geom.prefixRows;
|
|
11949
|
-
if (
|
|
12238
|
+
if (prevPrefix && prevPrefix.length > 1) {
|
|
11950
12239
|
const prevTotal = Math.max(0, Number(geom.totalRows) || 0);
|
|
11951
12240
|
const prevView = Math.max(1, Number(geom.viewRows) || 1);
|
|
11952
12241
|
const prevOffset = Math.max(0, Number(geom.renderOffset) || 0);
|
|
@@ -11957,7 +12246,7 @@ function useTranscriptWindow({
|
|
|
11957
12246
|
if (idx > prevPrefix.length - 2) idx = prevPrefix.length - 2;
|
|
11958
12247
|
const anchorItem = prevItems[idx];
|
|
11959
12248
|
if (anchorItem && anchorItem.id != null) {
|
|
11960
|
-
const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - (prevPrefix
|
|
12249
|
+
const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - transcriptRowAt(prevPrefix, idx)) };
|
|
11961
12250
|
const locked = resolveAnchorScrollOffset({
|
|
11962
12251
|
anchor: captured,
|
|
11963
12252
|
items: transcriptItems,
|
|
@@ -11979,7 +12268,7 @@ function useTranscriptWindow({
|
|
|
11979
12268
|
if (viewportOnlyChanged && !anchorLockActive && !followingRef.current && !nearBottomWithoutAnchor && scrolledUp) {
|
|
11980
12269
|
const geom = transcriptGeomRef.current || {};
|
|
11981
12270
|
const prevPrefix = geom.prefixRows;
|
|
11982
|
-
if (
|
|
12271
|
+
if (prevPrefix && prevPrefix.length > 1) {
|
|
11983
12272
|
const prevTotal = Math.max(0, Number(geom.totalRows) || 0);
|
|
11984
12273
|
const prevView = Math.max(1, Number(prevViewport.contentHeight) || Number(geom.viewRows) || 1);
|
|
11985
12274
|
const prevOffset = Math.max(0, Number(geom.renderOffset) || 0);
|
|
@@ -11990,7 +12279,7 @@ function useTranscriptWindow({
|
|
|
11990
12279
|
if (idx > prevPrefix.length - 2) idx = prevPrefix.length - 2;
|
|
11991
12280
|
const anchorItem = prevItems[idx];
|
|
11992
12281
|
if (anchorItem && anchorItem.id != null) {
|
|
11993
|
-
const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - (prevPrefix
|
|
12282
|
+
const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - transcriptRowAt(prevPrefix, idx)) };
|
|
11994
12283
|
const locked = resolveAnchorScrollOffset({
|
|
11995
12284
|
anchor: captured,
|
|
11996
12285
|
items: transcriptItems,
|
|
@@ -12212,7 +12501,7 @@ function useTranscriptWindow({
|
|
|
12212
12501
|
if (idx > itemList.length - 1) idx = itemList.length - 1;
|
|
12213
12502
|
const anchorItem = itemList[idx];
|
|
12214
12503
|
if (anchorItem && anchorItem.id != null) {
|
|
12215
|
-
anchor = { id: anchorItem.id, offset: Math.max(0, anchorRow - (curPrefix
|
|
12504
|
+
anchor = { id: anchorItem.id, offset: Math.max(0, anchorRow - transcriptRowAt(curPrefix, idx)) };
|
|
12216
12505
|
transcriptAnchorRef.current = anchor;
|
|
12217
12506
|
}
|
|
12218
12507
|
}
|
|
@@ -21657,9 +21946,11 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21657
21946
|
overlayHintFallbackRow,
|
|
21658
21947
|
transcriptMeasureRef
|
|
21659
21948
|
} = useTranscriptWindow({
|
|
21660
|
-
items: state.items,
|
|
21661
|
-
structureRevision: state.structureRevision,
|
|
21662
|
-
|
|
21949
|
+
items: state.transcriptViewItems || state.items,
|
|
21950
|
+
structureRevision: state.transcriptViewItems ? state.transcriptViewRevision : state.structureRevision,
|
|
21951
|
+
// Historical pages are contiguous settled windows. Keep the independently
|
|
21952
|
+
// growing live tail hidden until paging forward reaches the live window.
|
|
21953
|
+
streamingTail: state.transcriptViewItems ? null : state.streamingTail,
|
|
21663
21954
|
themeEpoch: state.themeEpoch,
|
|
21664
21955
|
frameColumns,
|
|
21665
21956
|
toolOutputExpanded,
|
|
@@ -21812,7 +22103,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21812
22103
|
Item,
|
|
21813
22104
|
{
|
|
21814
22105
|
item,
|
|
21815
|
-
prevKind: i > 0 ? arr[i - 1].kind : state.items[transcriptWindow.startIndex - 1]?.kind ?? null,
|
|
22106
|
+
prevKind: i > 0 ? arr[i - 1].kind : (state.transcriptViewItems || state.items)[transcriptWindow.startIndex - 1]?.kind ?? null,
|
|
21816
22107
|
columns: frameColumns,
|
|
21817
22108
|
toolOutputExpanded,
|
|
21818
22109
|
rightMessage: attachOverlayHint ? inputHint : "",
|
|
@@ -22094,6 +22385,11 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
22094
22385
|
|
|
22095
22386
|
// src/tui/engine.mjs
|
|
22096
22387
|
import { performance as performance2 } from "node:perf_hooks";
|
|
22388
|
+
import { mkdtempSync, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
22389
|
+
import { randomUUID } from "node:crypto";
|
|
22390
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
22391
|
+
import { join as join10 } from "node:path";
|
|
22392
|
+
import { Worker } from "node:worker_threads";
|
|
22097
22393
|
|
|
22098
22394
|
// src/runtime/shared/tool-execution-contract.mjs
|
|
22099
22395
|
function clean2(value) {
|
|
@@ -23027,71 +23323,6 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
23027
23323
|
};
|
|
23028
23324
|
}
|
|
23029
23325
|
|
|
23030
|
-
// src/tui/engine/render-timing.mjs
|
|
23031
|
-
var RENDER_ACK_HANG_GUARD_MS = 250;
|
|
23032
|
-
var RENDER_SETTLE_IDLE_MS = 64;
|
|
23033
|
-
var pendingRenderAcks = [];
|
|
23034
|
-
var renderAckSeq = 0;
|
|
23035
|
-
var scheduleRenderFrameAck = () => {
|
|
23036
|
-
const seq = ++renderAckSeq;
|
|
23037
|
-
setImmediate(() => notifyRenderFrame(seq));
|
|
23038
|
-
};
|
|
23039
|
-
var notifyRenderFrame = (seq = ++renderAckSeq) => {
|
|
23040
|
-
if (pendingRenderAcks.length === 0) return;
|
|
23041
|
-
const acks = pendingRenderAcks;
|
|
23042
|
-
pendingRenderAcks = [];
|
|
23043
|
-
for (const ack of acks) ack(seq);
|
|
23044
|
-
};
|
|
23045
|
-
var yieldToRenderer = ({ frames = 1 } = {}) => new Promise((resolve5) => {
|
|
23046
|
-
const minSeq = renderAckSeq;
|
|
23047
|
-
let remainingFrames = Math.max(1, Math.floor(Number(frames) || 1));
|
|
23048
|
-
let settled = false;
|
|
23049
|
-
let sawRealFrame = false;
|
|
23050
|
-
let timer2 = null;
|
|
23051
|
-
const finish = () => {
|
|
23052
|
-
if (settled) return;
|
|
23053
|
-
settled = true;
|
|
23054
|
-
if (timer2) clearTimeout(timer2);
|
|
23055
|
-
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
23056
|
-
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
23057
|
-
resolve5();
|
|
23058
|
-
};
|
|
23059
|
-
const onTimeout = () => {
|
|
23060
|
-
if (settled) return;
|
|
23061
|
-
if (!sawRealFrame && renderAckSeq > minSeq) {
|
|
23062
|
-
setImmediate(() => {
|
|
23063
|
-
if (!settled && !sawRealFrame) finish();
|
|
23064
|
-
});
|
|
23065
|
-
return;
|
|
23066
|
-
}
|
|
23067
|
-
finish();
|
|
23068
|
-
};
|
|
23069
|
-
const armWait = () => {
|
|
23070
|
-
if (settled) return;
|
|
23071
|
-
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
23072
|
-
if (timer2) clearTimeout(timer2);
|
|
23073
|
-
timer2 = setTimeout(onTimeout, sawRealFrame ? RENDER_SETTLE_IDLE_MS : RENDER_ACK_HANG_GUARD_MS);
|
|
23074
|
-
};
|
|
23075
|
-
const onFrame = (seq = 0) => {
|
|
23076
|
-
if (settled) return;
|
|
23077
|
-
if (seq <= minSeq) {
|
|
23078
|
-
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
23079
|
-
return;
|
|
23080
|
-
}
|
|
23081
|
-
sawRealFrame = true;
|
|
23082
|
-
if (timer2) {
|
|
23083
|
-
clearTimeout(timer2);
|
|
23084
|
-
timer2 = null;
|
|
23085
|
-
}
|
|
23086
|
-
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
23087
|
-
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
23088
|
-
remainingFrames -= 1;
|
|
23089
|
-
if (remainingFrames <= 0) finish();
|
|
23090
|
-
else armWait();
|
|
23091
|
-
};
|
|
23092
|
-
armWait();
|
|
23093
|
-
});
|
|
23094
|
-
|
|
23095
23326
|
// src/tui/engine/tool-result-status.mjs
|
|
23096
23327
|
var CANCELLED_RESULT_STATUS_LINE = "[status: cancelled]";
|
|
23097
23328
|
function shellCommandExitCode(text) {
|
|
@@ -24151,7 +24382,7 @@ function drainTuiSteeringPersist(leadSessionId) {
|
|
|
24151
24382
|
}
|
|
24152
24383
|
|
|
24153
24384
|
// src/tui/engine/context-state.mjs
|
|
24154
|
-
function createContextState({ runtime, getState, getPendingSessionReset }) {
|
|
24385
|
+
function createContextState({ runtime, getState, updateState, getPendingSessionReset }) {
|
|
24155
24386
|
const autoClearState = () => runtime.getAutoClear?.() || runtime.autoClear || { enabled: true, idleMs: 60 * 60 * 1e3, custom: false, providerDefault: 60 * 60 * 1e3, provider: null, minContextPercent: 10 };
|
|
24156
24387
|
const AGENT_STATUS_CACHE_MS = 250;
|
|
24157
24388
|
let agentStatusCache = null;
|
|
@@ -24201,15 +24432,16 @@ function createContextState({ runtime, getState, getPendingSessionReset }) {
|
|
|
24201
24432
|
function syncContextDisplayFields(ctx = null) {
|
|
24202
24433
|
const status = ctx || runtime.contextStatus?.() || null;
|
|
24203
24434
|
if (!status) return;
|
|
24204
|
-
const state = getState();
|
|
24205
24435
|
const displayWindow = Number(status.contextWindow || 0);
|
|
24206
24436
|
const compactBoundary = Number(status.compaction?.boundaryTokens || 0);
|
|
24207
24437
|
const autoCompact = Number(
|
|
24208
24438
|
status.compaction?.triggerTokens || status.compaction?.autoCompactTokenLimit || runtime.session?.autoCompactTokenLimit || 0
|
|
24209
24439
|
);
|
|
24210
|
-
|
|
24211
|
-
if (
|
|
24212
|
-
if (
|
|
24440
|
+
const patch = {};
|
|
24441
|
+
if (displayWindow > 0) patch.displayContextWindow = displayWindow;
|
|
24442
|
+
if (compactBoundary > 0) patch.compactBoundaryTokens = compactBoundary;
|
|
24443
|
+
if (autoCompact > 0) patch.autoCompactTokenLimit = autoCompact;
|
|
24444
|
+
if (Object.keys(patch).length > 0) updateState(patch);
|
|
24213
24445
|
}
|
|
24214
24446
|
const syncContextStats = ({ allowEstimated = false } = {}) => {
|
|
24215
24447
|
if (getPendingSessionReset()) return null;
|
|
@@ -24217,15 +24449,17 @@ function createContextState({ runtime, getState, getPendingSessionReset }) {
|
|
|
24217
24449
|
if (!ctx) return null;
|
|
24218
24450
|
syncContextDisplayFields(ctx);
|
|
24219
24451
|
const state = getState();
|
|
24220
|
-
const
|
|
24452
|
+
const stats = { ...state.stats };
|
|
24453
|
+
const hasProviderUsage = Number(stats.latestPromptTokens || stats.latestInputTokens || stats.inputTokens || 0) > 0;
|
|
24221
24454
|
const hasApiContextUsage = Number(ctx?.lastApiRequestTokens ?? ctx?.usage?.lastContextTokens ?? 0) > 0;
|
|
24222
24455
|
const hasTurnActivity = state.busy === true || state.spinner != null || state.thinking != null;
|
|
24223
24456
|
const isFreshSession = !hasProviderUsage && !hasApiContextUsage && !hasTurnActivity;
|
|
24224
24457
|
if (isFreshSession) {
|
|
24225
|
-
|
|
24226
|
-
|
|
24227
|
-
|
|
24228
|
-
|
|
24458
|
+
stats.currentEstimatedContextTokens = 0;
|
|
24459
|
+
stats.currentContextTokens = 0;
|
|
24460
|
+
stats.currentContextSource = null;
|
|
24461
|
+
stats.currentContextUpdatedAt = Date.now();
|
|
24462
|
+
updateState({ stats });
|
|
24229
24463
|
return ctx;
|
|
24230
24464
|
}
|
|
24231
24465
|
const estimatedTokens = Math.max(0, Number(ctx.currentEstimatedTokens ?? ctx.usedTokens ?? 0));
|
|
@@ -24234,32 +24468,33 @@ function createContextState({ runtime, getState, getPendingSessionReset }) {
|
|
|
24234
24468
|
const shouldPublishEstimate = allowEstimated && (usedSource === "estimated" || Number(ctx.currentEstimatedTokens) > 0 || usedTokens > 0);
|
|
24235
24469
|
if (!allowEstimated && !hasProviderUsage && usedSource !== "last_api_request") return ctx;
|
|
24236
24470
|
if (shouldPublishEstimate) {
|
|
24237
|
-
|
|
24238
|
-
|
|
24239
|
-
|
|
24471
|
+
stats.currentEstimatedContextTokens = estimatedTokens;
|
|
24472
|
+
stats.currentContextSource = "estimated";
|
|
24473
|
+
stats.currentContextTokens = 0;
|
|
24240
24474
|
} else if (allowEstimated && (hasProviderUsage || hasApiContextUsage || hasTurnActivity)) {
|
|
24241
|
-
|
|
24242
|
-
|
|
24243
|
-
const publishedSource = String(
|
|
24475
|
+
stats.currentEstimatedContextTokens = estimatedTokens;
|
|
24476
|
+
stats.currentContextSource = usedSource || (estimatedTokens > 0 ? "estimated" : null);
|
|
24477
|
+
const publishedSource = String(stats.currentContextSource || "").toLowerCase();
|
|
24244
24478
|
if (publishedSource === "last_api_request") {
|
|
24245
24479
|
const apiUsed = Math.max(0, Number(ctx.lastApiRequestTokens ?? usedTokens ?? 0));
|
|
24246
|
-
|
|
24480
|
+
stats.currentContextTokens = apiUsed;
|
|
24247
24481
|
} else if (publishedSource === "estimated") {
|
|
24248
|
-
|
|
24482
|
+
stats.currentContextTokens = 0;
|
|
24249
24483
|
} else {
|
|
24250
|
-
|
|
24484
|
+
stats.currentContextTokens = usedTokens > 0 ? usedTokens : 0;
|
|
24251
24485
|
}
|
|
24252
24486
|
} else {
|
|
24253
|
-
|
|
24487
|
+
stats.currentEstimatedContextTokens = 0;
|
|
24254
24488
|
if (usedSource === "last_api_request" && Number(ctx.lastApiRequestTokens ?? usedTokens ?? 0) > 0) {
|
|
24255
|
-
|
|
24256
|
-
|
|
24489
|
+
stats.currentContextTokens = Math.max(0, Number(ctx.lastApiRequestTokens ?? usedTokens ?? 0));
|
|
24490
|
+
stats.currentContextSource = "last_api_request";
|
|
24257
24491
|
} else {
|
|
24258
|
-
|
|
24259
|
-
|
|
24492
|
+
stats.currentContextTokens = 0;
|
|
24493
|
+
stats.currentContextSource = null;
|
|
24260
24494
|
}
|
|
24261
24495
|
}
|
|
24262
|
-
|
|
24496
|
+
stats.currentContextUpdatedAt = Date.now();
|
|
24497
|
+
updateState({ stats });
|
|
24263
24498
|
return ctx;
|
|
24264
24499
|
};
|
|
24265
24500
|
return {
|
|
@@ -24305,6 +24540,7 @@ function createSessionFlow(bag) {
|
|
|
24305
24540
|
clearToastTimers,
|
|
24306
24541
|
getState,
|
|
24307
24542
|
set,
|
|
24543
|
+
flushEmitImmediate,
|
|
24308
24544
|
pushItem,
|
|
24309
24545
|
replaceItems,
|
|
24310
24546
|
pushNotice,
|
|
@@ -24313,7 +24549,10 @@ function createSessionFlow(bag) {
|
|
|
24313
24549
|
agentStatusState,
|
|
24314
24550
|
routeState,
|
|
24315
24551
|
syncContextStats,
|
|
24316
|
-
flushDeferredExecutionPendingResumeKick
|
|
24552
|
+
flushDeferredExecutionPendingResumeKick,
|
|
24553
|
+
snapshotTranscriptSpill,
|
|
24554
|
+
restoreTranscriptSpill,
|
|
24555
|
+
releaseTranscriptSpill
|
|
24317
24556
|
} = bag;
|
|
24318
24557
|
const AUTO_CLEAR_COMPACT_TIMEOUT_MS = 6e4;
|
|
24319
24558
|
const leadSessionId = () => runtime.id;
|
|
@@ -24516,7 +24755,10 @@ function createSessionFlow(bag) {
|
|
|
24516
24755
|
if (getState().busy && shouldMirrorSteeringEntry(entry)) {
|
|
24517
24756
|
appendTuiSteeringPersist(leadSessionId(), entry);
|
|
24518
24757
|
}
|
|
24519
|
-
if (isQueuedEntryVisible(entry))
|
|
24758
|
+
if (isQueuedEntryVisible(entry)) {
|
|
24759
|
+
set({ queued: [...getState().queued, entry] });
|
|
24760
|
+
if (isQueuedEntryEditable(entry)) flushEmitImmediate?.();
|
|
24761
|
+
}
|
|
24520
24762
|
if (getState().busy) tuiDebug2(`busy-queue enqueue mode=${entry.mode} pending=${pending.length}`);
|
|
24521
24763
|
void drain();
|
|
24522
24764
|
return true;
|
|
@@ -24698,19 +24940,23 @@ function createSessionFlow(bag) {
|
|
|
24698
24940
|
return { count: queued.length, text: combinedText, pastedImages: mergePastedImages(queued), pastedTexts: mergePastedTexts(queued) };
|
|
24699
24941
|
}
|
|
24700
24942
|
const resetStats = () => {
|
|
24701
|
-
|
|
24702
|
-
|
|
24943
|
+
const stats = createSessionStats();
|
|
24944
|
+
set({ stats });
|
|
24945
|
+
return stats;
|
|
24703
24946
|
};
|
|
24704
24947
|
const clearUiActivityBeforeContextSync = () => {
|
|
24705
24948
|
clearToastTimers();
|
|
24706
24949
|
resetAllStreamingMarkdownStablePrefixes();
|
|
24707
|
-
|
|
24708
|
-
|
|
24709
|
-
|
|
24710
|
-
|
|
24711
|
-
|
|
24712
|
-
|
|
24713
|
-
|
|
24950
|
+
const items = replaceItems([]);
|
|
24951
|
+
set({
|
|
24952
|
+
items,
|
|
24953
|
+
toasts: [],
|
|
24954
|
+
queued: [],
|
|
24955
|
+
thinking: null,
|
|
24956
|
+
spinner: null,
|
|
24957
|
+
lastTurn: null,
|
|
24958
|
+
busy: false
|
|
24959
|
+
});
|
|
24714
24960
|
pendingNotificationKeys.clear();
|
|
24715
24961
|
displayedExecutionNotificationKeys.clear();
|
|
24716
24962
|
clearExecutionDedupState?.();
|
|
@@ -24743,16 +24989,24 @@ function createSessionFlow(bag) {
|
|
|
24743
24989
|
flags.pendingSessionReset = true;
|
|
24744
24990
|
clearUiActivityBeforeContextSync();
|
|
24745
24991
|
resetStats();
|
|
24746
|
-
|
|
24747
|
-
|
|
24748
|
-
|
|
24749
|
-
|
|
24750
|
-
|
|
24751
|
-
|
|
24752
|
-
|
|
24992
|
+
set({
|
|
24993
|
+
stats: {
|
|
24994
|
+
...getState().stats,
|
|
24995
|
+
currentContextTokens: 0,
|
|
24996
|
+
currentEstimatedContextTokens: 0,
|
|
24997
|
+
currentContextSource: null,
|
|
24998
|
+
currentContextUpdatedAt: Date.now()
|
|
24999
|
+
},
|
|
25000
|
+
displayContextWindow: 0,
|
|
25001
|
+
compactBoundaryTokens: 0,
|
|
25002
|
+
autoCompactTokenLimit: 0
|
|
25003
|
+
});
|
|
24753
25004
|
};
|
|
24754
25005
|
const snapshotTuiBeforeSessionReset = () => ({
|
|
24755
25006
|
items: getState().items.slice(),
|
|
25007
|
+
transcriptViewItems: Array.isArray(getState().transcriptViewItems) ? getState().transcriptViewItems.slice() : null,
|
|
25008
|
+
transcriptViewRevision: getState().transcriptViewRevision,
|
|
25009
|
+
transcriptSpill: snapshotTranscriptSpill?.() || null,
|
|
24756
25010
|
toasts: getState().toasts.slice(),
|
|
24757
25011
|
queued: getState().queued.slice(),
|
|
24758
25012
|
thinking: getState().thinking,
|
|
@@ -24765,14 +25019,20 @@ function createSessionFlow(bag) {
|
|
|
24765
25019
|
const restoreTuiAfterFailedSessionReset = (snapshot) => {
|
|
24766
25020
|
if (!snapshot) return;
|
|
24767
25021
|
flags.pendingSessionReset = false;
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
24772
|
-
|
|
24773
|
-
|
|
24774
|
-
|
|
24775
|
-
|
|
25022
|
+
restoreTranscriptSpill?.(snapshot.transcriptSpill);
|
|
25023
|
+
const items = replaceItems(snapshot.items, { preserveSpill: true });
|
|
25024
|
+
set({
|
|
25025
|
+
items,
|
|
25026
|
+
transcriptViewItems: snapshot.transcriptViewItems,
|
|
25027
|
+
transcriptViewRevision: snapshot.transcriptViewRevision,
|
|
25028
|
+
toasts: snapshot.toasts.slice(),
|
|
25029
|
+
queued: snapshot.queued.slice(),
|
|
25030
|
+
thinking: snapshot.thinking,
|
|
25031
|
+
spinner: snapshot.spinner,
|
|
25032
|
+
lastTurn: snapshot.lastTurn,
|
|
25033
|
+
busy: snapshot.busy,
|
|
25034
|
+
stats: { ...snapshot.stats }
|
|
25035
|
+
});
|
|
24776
25036
|
syncContextStats({ allowEstimated: true });
|
|
24777
25037
|
set({
|
|
24778
25038
|
items: getState().items,
|
|
@@ -24787,12 +25047,15 @@ function createSessionFlow(bag) {
|
|
|
24787
25047
|
...agentStatusState()
|
|
24788
25048
|
});
|
|
24789
25049
|
};
|
|
25050
|
+
const commitTuiSessionReset = (snapshot) => {
|
|
25051
|
+
releaseTranscriptSpill?.(snapshot?.transcriptSpill);
|
|
25052
|
+
};
|
|
24790
25053
|
const resetStatsAndSyncContext = () => {
|
|
24791
25054
|
resetStats();
|
|
24792
25055
|
syncContextStats({ allowEstimated: true });
|
|
24793
25056
|
return getState().stats;
|
|
24794
25057
|
};
|
|
24795
|
-
return { leadSessionId, shouldMirrorSteeringEntry, commitSteeringQueueEntries, makeQueueEntry, removeQueuedEntries, requeueEntriesFront, dequeueQueueBatch, drain, enqueue, drainPendingSteering, restoreLeadSteeringFromDisk, autoClearBeforeSubmit, performSessionClear, restoreQueued, resetStats, clearUiActivityBeforeContextSync, resetTuiForPendingSessionReset, snapshotTuiBeforeSessionReset, restoreTuiAfterFailedSessionReset, resetStatsAndSyncContext };
|
|
25058
|
+
return { leadSessionId, shouldMirrorSteeringEntry, commitSteeringQueueEntries, makeQueueEntry, removeQueuedEntries, requeueEntriesFront, dequeueQueueBatch, drain, enqueue, drainPendingSteering, restoreLeadSteeringFromDisk, autoClearBeforeSubmit, performSessionClear, restoreQueued, resetStats, clearUiActivityBeforeContextSync, resetTuiForPendingSessionReset, snapshotTuiBeforeSessionReset, restoreTuiAfterFailedSessionReset, commitTuiSessionReset, resetStatsAndSyncContext };
|
|
24796
25059
|
}
|
|
24797
25060
|
|
|
24798
25061
|
// src/tui/engine/turn.mjs
|
|
@@ -24807,7 +25070,10 @@ function createRunTurn(bag) {
|
|
|
24807
25070
|
itemIndexById,
|
|
24808
25071
|
getState,
|
|
24809
25072
|
set,
|
|
25073
|
+
flushEmit,
|
|
25074
|
+
flushEmitImmediate,
|
|
24810
25075
|
pushItem,
|
|
25076
|
+
appendItems,
|
|
24811
25077
|
patchItem,
|
|
24812
25078
|
replaceItems,
|
|
24813
25079
|
updateStreamingTail: updateStreamingTailFromStore,
|
|
@@ -24976,6 +25242,7 @@ function createRunTurn(bag) {
|
|
|
24976
25242
|
if (flags.draining) flags.draining = false;
|
|
24977
25243
|
if (pending.length > 0) void drain();
|
|
24978
25244
|
flushDeferredExecutionPendingResumeKick();
|
|
25245
|
+
flushEmitImmediate?.();
|
|
24979
25246
|
}, 5e3);
|
|
24980
25247
|
watchdogGraceTimer.unref?.();
|
|
24981
25248
|
}, delay);
|
|
@@ -25128,6 +25395,10 @@ function createRunTurn(bag) {
|
|
|
25128
25395
|
set(extra);
|
|
25129
25396
|
return;
|
|
25130
25397
|
}
|
|
25398
|
+
if (appendItems) {
|
|
25399
|
+
appendItems(newItems, extra);
|
|
25400
|
+
return;
|
|
25401
|
+
}
|
|
25131
25402
|
const base = getState().items.length;
|
|
25132
25403
|
const items = [...getState().items, ...newItems];
|
|
25133
25404
|
for (let i = 0; i < newItems.length; i++) {
|
|
@@ -25637,6 +25908,37 @@ function createRunTurn(bag) {
|
|
|
25637
25908
|
if (thinkingLastEndedAt) _pendingThinkingLastEndedAt = thinkingLastEndedAt;
|
|
25638
25909
|
scheduleStreamFlush();
|
|
25639
25910
|
},
|
|
25911
|
+
onTextReset: ({ chars } = {}) => {
|
|
25912
|
+
if (!isCurrentTurn()) return false;
|
|
25913
|
+
const count = Math.max(0, Number(chars) || 0);
|
|
25914
|
+
if (!count) return false;
|
|
25915
|
+
flushStreamBatch();
|
|
25916
|
+
assistantText = assistantText.slice(0, Math.max(0, assistantText.length - count));
|
|
25917
|
+
currentAssistantText = currentAssistantText.slice(
|
|
25918
|
+
0,
|
|
25919
|
+
Math.max(0, currentAssistantText.length - count)
|
|
25920
|
+
);
|
|
25921
|
+
_streamScanLen = 0;
|
|
25922
|
+
_lastNewlineIdx = -1;
|
|
25923
|
+
_emittedNewlineIdx = -2;
|
|
25924
|
+
_emittedVisibleText = "";
|
|
25925
|
+
if (currentAssistantId) {
|
|
25926
|
+
if (currentAssistantText) {
|
|
25927
|
+
set({
|
|
25928
|
+
streamingTail: {
|
|
25929
|
+
kind: "assistant",
|
|
25930
|
+
id: currentAssistantId,
|
|
25931
|
+
text: currentAssistantText,
|
|
25932
|
+
streaming: true
|
|
25933
|
+
}
|
|
25934
|
+
});
|
|
25935
|
+
} else {
|
|
25936
|
+
clearStreamingTail(currentAssistantId);
|
|
25937
|
+
currentAssistantId = null;
|
|
25938
|
+
}
|
|
25939
|
+
}
|
|
25940
|
+
return true;
|
|
25941
|
+
},
|
|
25640
25942
|
onAssistantText: (text) => {
|
|
25641
25943
|
const full = String(text ?? "");
|
|
25642
25944
|
if (!full.trim()) return;
|
|
@@ -25667,7 +25969,9 @@ function createRunTurn(bag) {
|
|
|
25667
25969
|
},
|
|
25668
25970
|
onUsageDelta: (delta) => {
|
|
25669
25971
|
if (!markTurnProgress("usage-delta")) return;
|
|
25670
|
-
|
|
25972
|
+
const stats = { ...getState().stats };
|
|
25973
|
+
applyUsageDelta(stats, delta);
|
|
25974
|
+
set({ stats });
|
|
25671
25975
|
syncContextStats({ allowEstimated: true });
|
|
25672
25976
|
const currentTurnInput = Math.max(0, getState().stats.inputTokens - inputBaseline);
|
|
25673
25977
|
const currentTurnOutput = Math.max(0, getState().stats.outputTokens - outputBaseline);
|
|
@@ -25770,7 +26074,7 @@ function createRunTurn(bag) {
|
|
|
25770
26074
|
const assistantOutput = (currentAssistantText || assistantText || "").trim();
|
|
25771
26075
|
const isNoOpTurn = turnFinishedNormally && !cancelled && toolCards.length === 0 && !resultContent && !assistantOutput && !producedTranscriptItem;
|
|
25772
26076
|
if (!isNoOpTurn) {
|
|
25773
|
-
getState().stats
|
|
26077
|
+
set({ stats: { ...getState().stats, turns: (getState().stats.turns || 0) + 1 } });
|
|
25774
26078
|
}
|
|
25775
26079
|
if (!reclaimed && !isNoOpTurn) {
|
|
25776
26080
|
closingItems.push({ kind: "turndone", id: nextId2(), elapsedMs, status: turnStatus, outputTokens: finalOutputTokens, thinkingElapsedMs, verb: pickDoneVerb(turnIndex) });
|
|
@@ -25789,6 +26093,7 @@ function createRunTurn(bag) {
|
|
|
25789
26093
|
}
|
|
25790
26094
|
}
|
|
25791
26095
|
if (flags.leadTurnEpoch === turnEpoch) clearActiveToolSummary();
|
|
26096
|
+
flushEmit?.();
|
|
25792
26097
|
_publishedThinkingActive = false;
|
|
25793
26098
|
const finalStatus = cancelled ? "cancelled" : failed ? "failed" : "done";
|
|
25794
26099
|
tuiDebug2(`runTurn end turn=${turnIndex} status=${finalStatus} elapsedMs=${Date.now() - startedAt}${watchdogTripped ? " watchdogTripped=1" : ""} pending=${pending.length}`);
|
|
@@ -25807,11 +26112,13 @@ function createEngineApiB(bag) {
|
|
|
25807
26112
|
listeners,
|
|
25808
26113
|
getState,
|
|
25809
26114
|
set,
|
|
26115
|
+
disposeEmit,
|
|
25810
26116
|
replaceItems,
|
|
25811
26117
|
pushNotice,
|
|
25812
26118
|
removeNotice,
|
|
25813
26119
|
setProgressHint,
|
|
25814
26120
|
clearToastTimers,
|
|
26121
|
+
disposeTranscriptSpill,
|
|
25815
26122
|
routeState,
|
|
25816
26123
|
syncContextStats,
|
|
25817
26124
|
finishToolApproval,
|
|
@@ -25822,6 +26129,7 @@ function createEngineApiB(bag) {
|
|
|
25822
26129
|
resetTuiForPendingSessionReset,
|
|
25823
26130
|
snapshotTuiBeforeSessionReset,
|
|
25824
26131
|
restoreTuiAfterFailedSessionReset,
|
|
26132
|
+
commitTuiSessionReset,
|
|
25825
26133
|
resetStatsAndSyncContext
|
|
25826
26134
|
} = bag;
|
|
25827
26135
|
return {
|
|
@@ -26166,6 +26474,7 @@ function createEngineApiB(bag) {
|
|
|
26166
26474
|
flags.pendingSessionReset = false;
|
|
26167
26475
|
resetStatsAndSyncContext();
|
|
26168
26476
|
set({ items: replaceItems([]), toasts: [], queued: [], thinking: null, spinner: null, lastTurn: null, ...routeState(), stats: { ...getState().stats } });
|
|
26477
|
+
commitTuiSessionReset(rollbackSnapshot);
|
|
26169
26478
|
flags.lastUserActivityAt = Date.now();
|
|
26170
26479
|
return true;
|
|
26171
26480
|
} catch (error) {
|
|
@@ -26203,6 +26512,7 @@ function createEngineApiB(bag) {
|
|
|
26203
26512
|
...routeState(),
|
|
26204
26513
|
stats: { ...getState().stats }
|
|
26205
26514
|
});
|
|
26515
|
+
commitTuiSessionReset(rollbackSnapshot);
|
|
26206
26516
|
return true;
|
|
26207
26517
|
} catch (error) {
|
|
26208
26518
|
restoreTuiAfterFailedSessionReset(rollbackSnapshot);
|
|
@@ -26235,6 +26545,7 @@ function createEngineApiB(bag) {
|
|
|
26235
26545
|
flags.pendingSessionReset = false;
|
|
26236
26546
|
resetStatsAndSyncContext();
|
|
26237
26547
|
set({ items: replaceItems([]), toasts: [], queued: [], thinking: null, spinner: null, lastTurn: null, ...routeState(), stats: { ...getState().stats } });
|
|
26548
|
+
commitTuiSessionReset(rollbackSnapshot);
|
|
26238
26549
|
return true;
|
|
26239
26550
|
} catch (error) {
|
|
26240
26551
|
restoreTuiAfterFailedSessionReset(rollbackSnapshot);
|
|
@@ -26305,8 +26616,10 @@ function createEngineApiB(bag) {
|
|
|
26305
26616
|
},
|
|
26306
26617
|
dispose: async (reason = "cli-react-exit", options = {}) => {
|
|
26307
26618
|
if (flags.disposed) return;
|
|
26619
|
+
disposeEmit?.();
|
|
26308
26620
|
flags.disposed = true;
|
|
26309
26621
|
clearToastTimers();
|
|
26622
|
+
disposeTranscriptSpill?.();
|
|
26310
26623
|
try {
|
|
26311
26624
|
clearInterval(lifecycle.runtimePulseTimer);
|
|
26312
26625
|
} catch {
|
|
@@ -26531,10 +26844,14 @@ function createEngineApiA(bag) {
|
|
|
26531
26844
|
pending,
|
|
26532
26845
|
listeners,
|
|
26533
26846
|
getState,
|
|
26847
|
+
getPublishedState = getState,
|
|
26534
26848
|
set,
|
|
26849
|
+
flushEmitImmediate,
|
|
26535
26850
|
pushItem,
|
|
26536
26851
|
patchItem,
|
|
26537
26852
|
replaceItems,
|
|
26853
|
+
restoreOlderTranscript,
|
|
26854
|
+
restoreNewerTranscript,
|
|
26538
26855
|
settleStreamingTail,
|
|
26539
26856
|
clearStreamingTail,
|
|
26540
26857
|
pushNotice,
|
|
@@ -26554,8 +26871,10 @@ function createEngineApiA(bag) {
|
|
|
26554
26871
|
discardExecutionPendingResume
|
|
26555
26872
|
} = bag;
|
|
26556
26873
|
return {
|
|
26557
|
-
getState: () =>
|
|
26874
|
+
getState: () => getPublishedState(),
|
|
26558
26875
|
patchItem,
|
|
26876
|
+
restoreOlderTranscript,
|
|
26877
|
+
restoreNewerTranscript,
|
|
26559
26878
|
subscribe: (listener) => {
|
|
26560
26879
|
listeners.add(listener);
|
|
26561
26880
|
return () => listeners.delete(listener);
|
|
@@ -27063,7 +27382,10 @@ function createEngineApiA(bag) {
|
|
|
27063
27382
|
if (idSet.size > 0) {
|
|
27064
27383
|
const items = getState().items.filter((item) => !idSet.has(item?.id));
|
|
27065
27384
|
if (items.length !== getState().items.length) {
|
|
27066
|
-
patch.items = replaceItems(items
|
|
27385
|
+
patch.items = replaceItems(items, {
|
|
27386
|
+
preserveSpill: true,
|
|
27387
|
+
preserveTranscriptView: true
|
|
27388
|
+
});
|
|
27067
27389
|
}
|
|
27068
27390
|
}
|
|
27069
27391
|
set(patch);
|
|
@@ -27091,6 +27413,7 @@ function createEngineApiA(bag) {
|
|
|
27091
27413
|
flags.drainEpoch = (Number(flags.drainEpoch) || 0) + 1;
|
|
27092
27414
|
if (flags.draining) flags.draining = false;
|
|
27093
27415
|
pushNotice("Interrupt did not settle \u2014 input restored.", "warn", { transcript: true });
|
|
27416
|
+
flushEmitImmediate?.();
|
|
27094
27417
|
if (pending.length > 0 && typeof drain === "function") void drain();
|
|
27095
27418
|
if (typeof flushDeferredExecutionPendingResumeKick === "function") flushDeferredExecutionPendingResumeKick();
|
|
27096
27419
|
}, recoveryMs);
|
|
@@ -27100,6 +27423,68 @@ function createEngineApiA(bag) {
|
|
|
27100
27423
|
};
|
|
27101
27424
|
}
|
|
27102
27425
|
|
|
27426
|
+
// src/tui/engine/frame-batched-store.mjs
|
|
27427
|
+
function createFrameBatchedStorePublisher({
|
|
27428
|
+
getState,
|
|
27429
|
+
publishState,
|
|
27430
|
+
listeners,
|
|
27431
|
+
isDisposed = () => false,
|
|
27432
|
+
frameMs = 16,
|
|
27433
|
+
setTimer = setTimeout,
|
|
27434
|
+
clearTimer = clearTimeout,
|
|
27435
|
+
enqueueMicrotask = queueMicrotask
|
|
27436
|
+
}) {
|
|
27437
|
+
let timer2 = null;
|
|
27438
|
+
let emitPending = false;
|
|
27439
|
+
let structureChangePending = false;
|
|
27440
|
+
let immediatePending = false;
|
|
27441
|
+
const flush = () => {
|
|
27442
|
+
if (timer2 !== null) {
|
|
27443
|
+
clearTimer(timer2);
|
|
27444
|
+
timer2 = null;
|
|
27445
|
+
}
|
|
27446
|
+
immediatePending = false;
|
|
27447
|
+
if (!emitPending || isDisposed()) return false;
|
|
27448
|
+
emitPending = false;
|
|
27449
|
+
const current = getState();
|
|
27450
|
+
let next = current;
|
|
27451
|
+
if (structureChangePending) {
|
|
27452
|
+
structureChangePending = false;
|
|
27453
|
+
next = {
|
|
27454
|
+
...current,
|
|
27455
|
+
structureRevision: (Number(current.structureRevision) || 0) + 1
|
|
27456
|
+
};
|
|
27457
|
+
}
|
|
27458
|
+
publishState(next);
|
|
27459
|
+
for (const listener of listeners) listener();
|
|
27460
|
+
return true;
|
|
27461
|
+
};
|
|
27462
|
+
const emit = () => {
|
|
27463
|
+
emitPending = true;
|
|
27464
|
+
if (timer2 !== null || isDisposed()) return;
|
|
27465
|
+
timer2 = setTimer(flush, frameMs);
|
|
27466
|
+
timer2?.unref?.();
|
|
27467
|
+
};
|
|
27468
|
+
const markStructureChange = () => {
|
|
27469
|
+
structureChangePending = true;
|
|
27470
|
+
};
|
|
27471
|
+
const flushImmediate = () => {
|
|
27472
|
+
if (!emitPending || immediatePending || isDisposed()) return false;
|
|
27473
|
+
immediatePending = true;
|
|
27474
|
+
enqueueMicrotask(flush);
|
|
27475
|
+
return true;
|
|
27476
|
+
};
|
|
27477
|
+
const dispose = () => {
|
|
27478
|
+
if (emitPending && !isDisposed()) flush();
|
|
27479
|
+
else if (timer2 !== null) clearTimer(timer2);
|
|
27480
|
+
timer2 = null;
|
|
27481
|
+
emitPending = false;
|
|
27482
|
+
structureChangePending = false;
|
|
27483
|
+
immediatePending = false;
|
|
27484
|
+
};
|
|
27485
|
+
return { emit, flush, flushImmediate, markStructureChange, dispose };
|
|
27486
|
+
}
|
|
27487
|
+
|
|
27103
27488
|
// src/tui/engine.mjs
|
|
27104
27489
|
var SESSION_RUNTIME_MODULE = import.meta.url.replace(/\\/g, "/").includes("/tui/dist/") ? "../../mixdog-session-runtime.mjs" : "../mixdog-session-runtime.mjs";
|
|
27105
27490
|
var TOOL_APPROVAL_TIMEOUT_MS = (() => {
|
|
@@ -27121,6 +27506,377 @@ var tuiDebug = (msg) => {
|
|
|
27121
27506
|
};
|
|
27122
27507
|
var _idSeq = 0;
|
|
27123
27508
|
var nextId = () => `it_${++_idSeq}`;
|
|
27509
|
+
var TRANSCRIPT_LIVE_ITEM_CAP = 512;
|
|
27510
|
+
var TRANSCRIPT_SPILL_CHUNK_ITEMS = 128;
|
|
27511
|
+
var TRANSCRIPT_RESTORE_OVERLAP_ITEMS = 64;
|
|
27512
|
+
var TRANSCRIPT_SPILL_STALE_MS = 24 * 60 * 60 * 1e3;
|
|
27513
|
+
var TRANSCRIPT_SPILL_HEARTBEAT_MS = 1e4;
|
|
27514
|
+
var TRANSCRIPT_PROCESS_NONCE = randomUUID();
|
|
27515
|
+
function cleanupStaleTranscriptSpillDirs({
|
|
27516
|
+
root = tmpdir2(),
|
|
27517
|
+
now = Date.now(),
|
|
27518
|
+
staleMs = TRANSCRIPT_SPILL_STALE_MS
|
|
27519
|
+
} = {}) {
|
|
27520
|
+
try {
|
|
27521
|
+
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
27522
|
+
if (!entry.isDirectory() || !entry.name.startsWith("mixdog-transcript-")) continue;
|
|
27523
|
+
const path2 = join10(root, entry.name);
|
|
27524
|
+
try {
|
|
27525
|
+
const ownerPid = Number(/^mixdog-transcript-(\d+)-/.exec(entry.name)?.[1]);
|
|
27526
|
+
let pidAlive = false;
|
|
27527
|
+
if (ownerPid > 0) {
|
|
27528
|
+
try {
|
|
27529
|
+
process.kill(ownerPid, 0);
|
|
27530
|
+
pidAlive = true;
|
|
27531
|
+
} catch {
|
|
27532
|
+
}
|
|
27533
|
+
}
|
|
27534
|
+
if (!pidAlive) {
|
|
27535
|
+
rmSync2(path2, { recursive: true, force: true });
|
|
27536
|
+
continue;
|
|
27537
|
+
}
|
|
27538
|
+
let heartbeatAge;
|
|
27539
|
+
try {
|
|
27540
|
+
heartbeatAge = now - statSync5(join10(path2, "heartbeat")).mtimeMs;
|
|
27541
|
+
} catch {
|
|
27542
|
+
heartbeatAge = now - statSync5(path2).mtimeMs;
|
|
27543
|
+
}
|
|
27544
|
+
if (heartbeatAge <= staleMs) continue;
|
|
27545
|
+
rmSync2(path2, { recursive: true, force: true });
|
|
27546
|
+
} catch {
|
|
27547
|
+
}
|
|
27548
|
+
}
|
|
27549
|
+
} catch {
|
|
27550
|
+
}
|
|
27551
|
+
}
|
|
27552
|
+
function createTranscriptSpillBuffer({
|
|
27553
|
+
cap = TRANSCRIPT_LIVE_ITEM_CAP,
|
|
27554
|
+
chunkSize = TRANSCRIPT_SPILL_CHUNK_ITEMS,
|
|
27555
|
+
workerFactory = (source) => new Worker(source, { eval: true }),
|
|
27556
|
+
onWarning = (message) => tuiDebug(message),
|
|
27557
|
+
writeTimeoutMs = 5e3
|
|
27558
|
+
} = {}) {
|
|
27559
|
+
try {
|
|
27560
|
+
writeFileSync6(
|
|
27561
|
+
join10(tmpdir2(), `mixdog-transcript-owner-${process.pid}.json`),
|
|
27562
|
+
JSON.stringify({ pid: process.pid, nonce: TRANSCRIPT_PROCESS_NONCE }),
|
|
27563
|
+
"utf8"
|
|
27564
|
+
);
|
|
27565
|
+
} catch {
|
|
27566
|
+
}
|
|
27567
|
+
cleanupStaleTranscriptSpillDirs();
|
|
27568
|
+
const pages = [];
|
|
27569
|
+
let cursor = null;
|
|
27570
|
+
let spillDir = null;
|
|
27571
|
+
let pageSequence = 0;
|
|
27572
|
+
let spillWorker = null;
|
|
27573
|
+
let workerSpawnCount = 0;
|
|
27574
|
+
let activeWrite = null;
|
|
27575
|
+
let activeWriteTimer = null;
|
|
27576
|
+
let warningEmitted = false;
|
|
27577
|
+
let spillDisabled = false;
|
|
27578
|
+
const writeQueue = [];
|
|
27579
|
+
const heartbeatTimers = /* @__PURE__ */ new Map();
|
|
27580
|
+
const snapshots = /* @__PURE__ */ new Set();
|
|
27581
|
+
const cleanupRecords = (records, directory) => {
|
|
27582
|
+
for (const record of records) {
|
|
27583
|
+
record.cancelled = true;
|
|
27584
|
+
}
|
|
27585
|
+
if (directory) {
|
|
27586
|
+
const timer2 = heartbeatTimers.get(directory);
|
|
27587
|
+
if (timer2) clearInterval(timer2);
|
|
27588
|
+
heartbeatTimers.delete(directory);
|
|
27589
|
+
try {
|
|
27590
|
+
rmSync2(directory, { recursive: true, force: true });
|
|
27591
|
+
} catch {
|
|
27592
|
+
}
|
|
27593
|
+
}
|
|
27594
|
+
};
|
|
27595
|
+
const ensureSpillDir = () => {
|
|
27596
|
+
if (spillDir) return spillDir;
|
|
27597
|
+
const root = tmpdir2();
|
|
27598
|
+
writeFileSync6(
|
|
27599
|
+
join10(root, `mixdog-transcript-owner-${process.pid}.json`),
|
|
27600
|
+
JSON.stringify({ pid: process.pid, nonce: TRANSCRIPT_PROCESS_NONCE }),
|
|
27601
|
+
"utf8"
|
|
27602
|
+
);
|
|
27603
|
+
spillDir = mkdtempSync(join10(root, `mixdog-transcript-${process.pid}-${TRANSCRIPT_PROCESS_NONCE}-`));
|
|
27604
|
+
writeFileSync6(
|
|
27605
|
+
join10(spillDir, "owner.json"),
|
|
27606
|
+
JSON.stringify({ pid: process.pid, nonce: TRANSCRIPT_PROCESS_NONCE }),
|
|
27607
|
+
"utf8"
|
|
27608
|
+
);
|
|
27609
|
+
const heartbeat = join10(spillDir, "heartbeat");
|
|
27610
|
+
writeFileSync6(heartbeat, String(Date.now()), "utf8");
|
|
27611
|
+
const heartbeatTimer = setInterval(() => {
|
|
27612
|
+
try {
|
|
27613
|
+
writeFileSync6(heartbeat, String(Date.now()), "utf8");
|
|
27614
|
+
} catch {
|
|
27615
|
+
}
|
|
27616
|
+
}, TRANSCRIPT_SPILL_HEARTBEAT_MS);
|
|
27617
|
+
heartbeatTimer.unref?.();
|
|
27618
|
+
heartbeatTimers.set(spillDir, heartbeatTimer);
|
|
27619
|
+
return spillDir;
|
|
27620
|
+
};
|
|
27621
|
+
const workerSource = `
|
|
27622
|
+
const { parentPort } = require('node:worker_threads');
|
|
27623
|
+
const { renameSync, writeFileSync } = require('node:fs');
|
|
27624
|
+
parentPort.on('message', ({ id, targetPath, tempPath, items }) => {
|
|
27625
|
+
try {
|
|
27626
|
+
writeFileSync(tempPath, JSON.stringify(items), 'utf8');
|
|
27627
|
+
renameSync(tempPath, targetPath);
|
|
27628
|
+
parentPort.postMessage({ id, ok: true });
|
|
27629
|
+
} catch (error) {
|
|
27630
|
+
parentPort.postMessage({ id, ok: false, error: String(error && error.message || error) });
|
|
27631
|
+
}
|
|
27632
|
+
});`;
|
|
27633
|
+
const ensureWorker = () => {
|
|
27634
|
+
if (spillWorker) return spillWorker;
|
|
27635
|
+
try {
|
|
27636
|
+
const worker = workerFactory(workerSource);
|
|
27637
|
+
spillWorker = worker;
|
|
27638
|
+
workerSpawnCount += 1;
|
|
27639
|
+
worker.on("message", (result) => {
|
|
27640
|
+
if (spillWorker !== worker || result?.id !== activeWrite?.id) return;
|
|
27641
|
+
finishWrite(result?.ok === true, result?.error);
|
|
27642
|
+
});
|
|
27643
|
+
const failWorker = (error) => {
|
|
27644
|
+
if (spillWorker !== worker) return;
|
|
27645
|
+
if (activeWriteTimer) clearTimeout(activeWriteTimer);
|
|
27646
|
+
activeWriteTimer = null;
|
|
27647
|
+
const failed = activeWrite;
|
|
27648
|
+
activeWrite = null;
|
|
27649
|
+
spillWorker = null;
|
|
27650
|
+
try {
|
|
27651
|
+
worker.terminate?.();
|
|
27652
|
+
} catch {
|
|
27653
|
+
}
|
|
27654
|
+
if (failed) retryOrPin(failed, error?.message);
|
|
27655
|
+
pumpWrites();
|
|
27656
|
+
};
|
|
27657
|
+
worker.on("error", failWorker);
|
|
27658
|
+
worker.on("exit", (code) => {
|
|
27659
|
+
failWorker(new Error(`spill worker exited (${code})`));
|
|
27660
|
+
});
|
|
27661
|
+
worker.unref?.();
|
|
27662
|
+
} catch (error) {
|
|
27663
|
+
spillWorker = null;
|
|
27664
|
+
if (activeWrite) {
|
|
27665
|
+
const failed = activeWrite;
|
|
27666
|
+
activeWrite = null;
|
|
27667
|
+
retryOrPin(failed, error?.message);
|
|
27668
|
+
}
|
|
27669
|
+
}
|
|
27670
|
+
return spillWorker;
|
|
27671
|
+
};
|
|
27672
|
+
const retryOrPin = (record, error) => {
|
|
27673
|
+
if (record.cancelled) return;
|
|
27674
|
+
record.attempts += 1;
|
|
27675
|
+
if (record.attempts <= 2) {
|
|
27676
|
+
writeQueue.unshift(record);
|
|
27677
|
+
return;
|
|
27678
|
+
}
|
|
27679
|
+
record.pinned = true;
|
|
27680
|
+
spillDisabled = true;
|
|
27681
|
+
for (const queued of writeQueue.splice(0)) {
|
|
27682
|
+
if (!queued.cancelled) queued.pinned = true;
|
|
27683
|
+
}
|
|
27684
|
+
if (!warningEmitted) {
|
|
27685
|
+
warningEmitted = true;
|
|
27686
|
+
try {
|
|
27687
|
+
onWarning(`transcript spill write failed; history pinned in memory (${error || "unknown error"})`);
|
|
27688
|
+
} catch {
|
|
27689
|
+
}
|
|
27690
|
+
}
|
|
27691
|
+
};
|
|
27692
|
+
const finishWrite = (ok, error) => {
|
|
27693
|
+
if (activeWriteTimer) clearTimeout(activeWriteTimer);
|
|
27694
|
+
activeWriteTimer = null;
|
|
27695
|
+
const record = activeWrite;
|
|
27696
|
+
activeWrite = null;
|
|
27697
|
+
if (record && !record.cancelled) {
|
|
27698
|
+
if (ok) record.pendingItems = null;
|
|
27699
|
+
else retryOrPin(record, error);
|
|
27700
|
+
}
|
|
27701
|
+
pumpWrites();
|
|
27702
|
+
};
|
|
27703
|
+
const pumpWrites = () => {
|
|
27704
|
+
if (activeWrite) return;
|
|
27705
|
+
while (writeQueue.length && writeQueue[0].cancelled) writeQueue.shift();
|
|
27706
|
+
if (!writeQueue.length) return;
|
|
27707
|
+
activeWrite = writeQueue.shift();
|
|
27708
|
+
const worker = ensureWorker();
|
|
27709
|
+
if (!worker) {
|
|
27710
|
+
if (activeWrite) {
|
|
27711
|
+
const failed = activeWrite;
|
|
27712
|
+
activeWrite = null;
|
|
27713
|
+
retryOrPin(failed, "worker unavailable");
|
|
27714
|
+
}
|
|
27715
|
+
queueMicrotask(pumpWrites);
|
|
27716
|
+
return;
|
|
27717
|
+
}
|
|
27718
|
+
const tempPath = `${activeWrite.path}.attempt-${activeWrite.attempts}-${randomUUID()}.tmp`;
|
|
27719
|
+
worker.postMessage({
|
|
27720
|
+
id: activeWrite.id,
|
|
27721
|
+
targetPath: activeWrite.path,
|
|
27722
|
+
tempPath,
|
|
27723
|
+
items: activeWrite.pendingItems
|
|
27724
|
+
});
|
|
27725
|
+
activeWriteTimer = setTimeout(() => {
|
|
27726
|
+
if (!activeWrite || spillWorker !== worker) return;
|
|
27727
|
+
const failed = activeWrite;
|
|
27728
|
+
activeWrite = null;
|
|
27729
|
+
activeWriteTimer = null;
|
|
27730
|
+
spillWorker = null;
|
|
27731
|
+
try {
|
|
27732
|
+
worker.terminate?.();
|
|
27733
|
+
} catch {
|
|
27734
|
+
}
|
|
27735
|
+
retryOrPin(failed, `write timed out after ${writeTimeoutMs}ms`);
|
|
27736
|
+
pumpWrites();
|
|
27737
|
+
}, Math.max(1, Number(writeTimeoutMs) || 5e3));
|
|
27738
|
+
activeWriteTimer.unref?.();
|
|
27739
|
+
};
|
|
27740
|
+
const encode = (items) => {
|
|
27741
|
+
const page = join10(ensureSpillDir(), `${++pageSequence}.json`);
|
|
27742
|
+
const record = {
|
|
27743
|
+
id: pageSequence,
|
|
27744
|
+
path: page,
|
|
27745
|
+
pendingItems: items,
|
|
27746
|
+
cancelled: false,
|
|
27747
|
+
attempts: 0,
|
|
27748
|
+
pinned: false
|
|
27749
|
+
};
|
|
27750
|
+
writeQueue.push(record);
|
|
27751
|
+
pumpWrites();
|
|
27752
|
+
return record;
|
|
27753
|
+
};
|
|
27754
|
+
const decode = (record) => record.pendingItems || JSON.parse(readFileSync8(record.path, "utf8"));
|
|
27755
|
+
return {
|
|
27756
|
+
get hasOlder() {
|
|
27757
|
+
return cursor == null ? pages.length > 0 : cursor > 0;
|
|
27758
|
+
},
|
|
27759
|
+
get hasNewer() {
|
|
27760
|
+
return cursor != null;
|
|
27761
|
+
},
|
|
27762
|
+
reset() {
|
|
27763
|
+
const retained = [...snapshots].some((snapshot) => snapshot.spillDir === spillDir);
|
|
27764
|
+
const oldPages = pages.splice(0);
|
|
27765
|
+
const oldDir = spillDir;
|
|
27766
|
+
pages.length = 0;
|
|
27767
|
+
cursor = null;
|
|
27768
|
+
spillDir = null;
|
|
27769
|
+
pageSequence = 0;
|
|
27770
|
+
spillDisabled = false;
|
|
27771
|
+
warningEmitted = false;
|
|
27772
|
+
if (!retained) cleanupRecords(oldPages, oldDir);
|
|
27773
|
+
},
|
|
27774
|
+
snapshot() {
|
|
27775
|
+
const snapshot = {
|
|
27776
|
+
pages: pages.slice(),
|
|
27777
|
+
cursor,
|
|
27778
|
+
spillDir,
|
|
27779
|
+
pageSequence,
|
|
27780
|
+
spillDisabled,
|
|
27781
|
+
warningEmitted
|
|
27782
|
+
};
|
|
27783
|
+
snapshots.add(snapshot);
|
|
27784
|
+
return snapshot;
|
|
27785
|
+
},
|
|
27786
|
+
restoreSnapshot(snapshot) {
|
|
27787
|
+
if (!snapshot || !snapshots.has(snapshot)) return false;
|
|
27788
|
+
if (snapshot.spillDir === spillDir) {
|
|
27789
|
+
cursor = snapshot.cursor;
|
|
27790
|
+
snapshots.delete(snapshot);
|
|
27791
|
+
return true;
|
|
27792
|
+
}
|
|
27793
|
+
cleanupRecords(pages, spillDir);
|
|
27794
|
+
pages.splice(0, pages.length, ...snapshot.pages);
|
|
27795
|
+
cursor = snapshot.cursor;
|
|
27796
|
+
spillDir = snapshot.spillDir;
|
|
27797
|
+
pageSequence = snapshot.pageSequence;
|
|
27798
|
+
spillDisabled = snapshot.spillDisabled === true;
|
|
27799
|
+
warningEmitted = snapshot.warningEmitted === true;
|
|
27800
|
+
snapshots.delete(snapshot);
|
|
27801
|
+
return true;
|
|
27802
|
+
},
|
|
27803
|
+
releaseSnapshot(snapshot) {
|
|
27804
|
+
if (!snapshot || !snapshots.delete(snapshot)) return false;
|
|
27805
|
+
if (snapshot.spillDir !== spillDir) cleanupRecords(snapshot.pages, snapshot.spillDir);
|
|
27806
|
+
return true;
|
|
27807
|
+
},
|
|
27808
|
+
dispose() {
|
|
27809
|
+
cleanupRecords(pages, spillDir);
|
|
27810
|
+
for (const snapshot of snapshots) {
|
|
27811
|
+
if (snapshot.spillDir !== spillDir) cleanupRecords(snapshot.pages, snapshot.spillDir);
|
|
27812
|
+
}
|
|
27813
|
+
pages.length = 0;
|
|
27814
|
+
snapshots.clear();
|
|
27815
|
+
cursor = null;
|
|
27816
|
+
spillDir = null;
|
|
27817
|
+
for (const timer2 of heartbeatTimers.values()) clearInterval(timer2);
|
|
27818
|
+
heartbeatTimers.clear();
|
|
27819
|
+
writeQueue.length = 0;
|
|
27820
|
+
activeWrite = null;
|
|
27821
|
+
if (activeWriteTimer) clearTimeout(activeWriteTimer);
|
|
27822
|
+
activeWriteTimer = null;
|
|
27823
|
+
try {
|
|
27824
|
+
spillWorker?.terminate();
|
|
27825
|
+
} catch {
|
|
27826
|
+
}
|
|
27827
|
+
spillWorker = null;
|
|
27828
|
+
},
|
|
27829
|
+
get workerCount() {
|
|
27830
|
+
return workerSpawnCount;
|
|
27831
|
+
},
|
|
27832
|
+
get pendingWriteCount() {
|
|
27833
|
+
return writeQueue.length + (activeWrite ? 1 : 0);
|
|
27834
|
+
},
|
|
27835
|
+
get pinnedPageCount() {
|
|
27836
|
+
return pages.filter((page) => page.pinned).length;
|
|
27837
|
+
},
|
|
27838
|
+
get disabled() {
|
|
27839
|
+
return spillDisabled;
|
|
27840
|
+
},
|
|
27841
|
+
capLive(items) {
|
|
27842
|
+
let live = Array.isArray(items) ? items : [];
|
|
27843
|
+
if (spillDisabled) return live;
|
|
27844
|
+
while (live.length > cap) {
|
|
27845
|
+
pages.push(encode(live.slice(0, chunkSize)));
|
|
27846
|
+
live = live.slice(chunkSize);
|
|
27847
|
+
}
|
|
27848
|
+
return live;
|
|
27849
|
+
},
|
|
27850
|
+
restoreOlder(liveItems) {
|
|
27851
|
+
const nextCursor = cursor == null ? pages.length - 1 : cursor - 1;
|
|
27852
|
+
if (nextCursor < 0) return null;
|
|
27853
|
+
cursor = nextCursor;
|
|
27854
|
+
const restored = decode(pages[cursor]);
|
|
27855
|
+
const following = cursor + 1 < pages.length ? decode(pages[cursor + 1]) : Array.isArray(liveItems) ? liveItems : [];
|
|
27856
|
+
return [...restored, ...following.slice(0, TRANSCRIPT_RESTORE_OVERLAP_ITEMS)];
|
|
27857
|
+
},
|
|
27858
|
+
restoreNewer(liveItems) {
|
|
27859
|
+
if (cursor == null) return null;
|
|
27860
|
+
const nextCursor = cursor + 1;
|
|
27861
|
+
if (nextCursor >= pages.length) {
|
|
27862
|
+
cursor = null;
|
|
27863
|
+
return { items: null, atLive: true };
|
|
27864
|
+
}
|
|
27865
|
+
cursor = nextCursor;
|
|
27866
|
+
const restored = decode(pages[cursor]);
|
|
27867
|
+
const following = cursor + 1 < pages.length ? decode(pages[cursor + 1]) : Array.isArray(liveItems) ? liveItems : [];
|
|
27868
|
+
return [...restored, ...following.slice(0, TRANSCRIPT_RESTORE_OVERLAP_ITEMS)];
|
|
27869
|
+
}
|
|
27870
|
+
};
|
|
27871
|
+
}
|
|
27872
|
+
function refillTranscriptViewOverlap(viewItems, previousLiveItems, nextLiveItems) {
|
|
27873
|
+
const view = Array.isArray(viewItems) ? viewItems : null;
|
|
27874
|
+
if (!view) return null;
|
|
27875
|
+
const previousIds = new Set((previousLiveItems || []).map((item) => item?.id).filter((id) => id != null));
|
|
27876
|
+
if (!view.some((item) => previousIds.has(item?.id))) return view;
|
|
27877
|
+
const historical = view.filter((item) => !previousIds.has(item?.id));
|
|
27878
|
+
return [...historical, ...(nextLiveItems || []).slice(0, TRANSCRIPT_RESTORE_OVERLAP_ITEMS)];
|
|
27879
|
+
}
|
|
27124
27880
|
function replaceEngineItemsState({
|
|
27125
27881
|
state,
|
|
27126
27882
|
items,
|
|
@@ -27142,7 +27898,13 @@ function replaceEngineItemsState({
|
|
|
27142
27898
|
streamingTail: preserveStreamingTail ? state.streamingTail : null
|
|
27143
27899
|
};
|
|
27144
27900
|
}
|
|
27145
|
-
function createEngineItemMutators({
|
|
27901
|
+
function createEngineItemMutators({
|
|
27902
|
+
getState,
|
|
27903
|
+
set,
|
|
27904
|
+
itemIndexById,
|
|
27905
|
+
normalizeItems = (items) => items,
|
|
27906
|
+
itemStateExtra = () => ({})
|
|
27907
|
+
}) {
|
|
27146
27908
|
const patchItem = (id, patch) => {
|
|
27147
27909
|
const state = getState();
|
|
27148
27910
|
let index = itemIndexById.get(id);
|
|
@@ -27182,15 +27944,21 @@ function createEngineItemMutators({ getState, set, itemIndexById }) {
|
|
|
27182
27944
|
streaming: false
|
|
27183
27945
|
};
|
|
27184
27946
|
const index = state.items.length;
|
|
27185
|
-
const items = [...state.items, item];
|
|
27186
|
-
itemIndexById.
|
|
27947
|
+
const items = normalizeItems([...state.items, item]);
|
|
27948
|
+
itemIndexById.clear();
|
|
27949
|
+
for (let i = 0; i < items.length; i++) {
|
|
27950
|
+
const itemId = items[i]?.id;
|
|
27951
|
+
if (itemId != null) itemIndexById.set(itemId, i);
|
|
27952
|
+
}
|
|
27953
|
+
const settledIndex = items.findIndex((entry) => entry?.id === id);
|
|
27187
27954
|
set({
|
|
27188
27955
|
items,
|
|
27189
27956
|
structureRevision: (Number(state.structureRevision) || 0) + 1,
|
|
27190
27957
|
streamingTail: null,
|
|
27958
|
+
...itemStateExtra(),
|
|
27191
27959
|
...extra
|
|
27192
27960
|
});
|
|
27193
|
-
return
|
|
27961
|
+
return settledIndex >= 0;
|
|
27194
27962
|
};
|
|
27195
27963
|
return { patchItem, settleStreamingTail };
|
|
27196
27964
|
}
|
|
@@ -27239,6 +28007,7 @@ async function createEngineSession({
|
|
|
27239
28007
|
const pendingNotificationKeys = /* @__PURE__ */ new Set();
|
|
27240
28008
|
const displayedExecutionNotificationKeys = /* @__PURE__ */ new Set();
|
|
27241
28009
|
const bag = {};
|
|
28010
|
+
let state;
|
|
27242
28011
|
const {
|
|
27243
28012
|
autoClearState,
|
|
27244
28013
|
agentStatusState,
|
|
@@ -27248,6 +28017,9 @@ async function createEngineSession({
|
|
|
27248
28017
|
} = createContextState({
|
|
27249
28018
|
runtime,
|
|
27250
28019
|
getState: () => state,
|
|
28020
|
+
updateState: (patch) => {
|
|
28021
|
+
state = { ...state, ...patch };
|
|
28022
|
+
},
|
|
27251
28023
|
getPendingSessionReset: () => flags.pendingSessionReset
|
|
27252
28024
|
});
|
|
27253
28025
|
const initialAgentState = {
|
|
@@ -27255,8 +28027,12 @@ async function createEngineSession({
|
|
|
27255
28027
|
agentJobs: [],
|
|
27256
28028
|
agentScope: null
|
|
27257
28029
|
};
|
|
27258
|
-
|
|
28030
|
+
state = {
|
|
27259
28031
|
items: [],
|
|
28032
|
+
transcriptViewItems: null,
|
|
28033
|
+
transcriptViewRevision: 0,
|
|
28034
|
+
transcriptHistoryBefore: false,
|
|
28035
|
+
transcriptHistoryAfter: false,
|
|
27260
28036
|
structureRevision: 0,
|
|
27261
28037
|
streamingTail: null,
|
|
27262
28038
|
toasts: [],
|
|
@@ -27295,50 +28071,109 @@ async function createEngineSession({
|
|
|
27295
28071
|
syncContextStats({ allowEstimated: true });
|
|
27296
28072
|
bootProfile("engine:context-ready", { ms: (performance2.now() - contextStartedAt).toFixed(1) });
|
|
27297
28073
|
const listeners = /* @__PURE__ */ new Set();
|
|
27298
|
-
let
|
|
27299
|
-
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
28074
|
+
let publishedState = process.env.NODE_ENV === "production" ? state : Object.freeze(state);
|
|
28075
|
+
state = { ...state, stats: { ...state.stats } };
|
|
28076
|
+
const publisher = createFrameBatchedStorePublisher({
|
|
28077
|
+
getState: () => state,
|
|
28078
|
+
publishState: (next) => {
|
|
28079
|
+
publishedState = process.env.NODE_ENV === "production" ? next : Object.freeze(next);
|
|
28080
|
+
state = { ...next, stats: { ...next.stats } };
|
|
28081
|
+
},
|
|
28082
|
+
listeners,
|
|
28083
|
+
isDisposed: () => flags.disposed
|
|
28084
|
+
});
|
|
28085
|
+
const emit = publisher.emit;
|
|
28086
|
+
const flushEmit = publisher.flush;
|
|
28087
|
+
const flushEmitImmediate = publisher.flushImmediate;
|
|
27308
28088
|
const set = (patch) => {
|
|
27309
28089
|
if (!patch || typeof patch !== "object") return false;
|
|
28090
|
+
const requestsStructureChange = Object.prototype.hasOwnProperty.call(patch, "structureRevision") && !Object.is(patch.structureRevision, state.structureRevision);
|
|
28091
|
+
const effectivePatch = requestsStructureChange ? Object.fromEntries(Object.entries(patch).filter(([key]) => key !== "structureRevision")) : patch;
|
|
27310
28092
|
let changed = false;
|
|
27311
|
-
for (const [key, value] of Object.entries(
|
|
28093
|
+
for (const [key, value] of Object.entries(effectivePatch)) {
|
|
27312
28094
|
if (!Object.is(state[key], value)) {
|
|
27313
28095
|
changed = true;
|
|
27314
28096
|
break;
|
|
27315
28097
|
}
|
|
27316
28098
|
}
|
|
27317
|
-
if (!changed) return false;
|
|
28099
|
+
if (!changed && !requestsStructureChange) return false;
|
|
27318
28100
|
const commandBusyReleased = state.commandBusy === true && Object.prototype.hasOwnProperty.call(patch, "commandBusy") && patch.commandBusy === false;
|
|
27319
|
-
state = { ...state, ...
|
|
28101
|
+
state = { ...state, ...effectivePatch };
|
|
28102
|
+
if (requestsStructureChange) publisher.markStructureChange();
|
|
27320
28103
|
emit();
|
|
28104
|
+
if (effectivePatch.commandStatus || effectivePatch.toolApproval) {
|
|
28105
|
+
flushEmitImmediate();
|
|
28106
|
+
}
|
|
27321
28107
|
if (commandBusyReleased) queueMicrotask(() => {
|
|
27322
28108
|
void bag.drain?.();
|
|
27323
28109
|
});
|
|
27324
28110
|
return true;
|
|
27325
28111
|
};
|
|
27326
28112
|
const itemIndexById = /* @__PURE__ */ new Map();
|
|
27327
|
-
const
|
|
28113
|
+
const transcriptSpill = createTranscriptSpillBuffer();
|
|
28114
|
+
const reindexLiveItems = (items) => {
|
|
28115
|
+
itemIndexById.clear();
|
|
28116
|
+
for (let i = 0; i < items.length; i++) {
|
|
28117
|
+
const id = items[i]?.id;
|
|
28118
|
+
if (id != null) itemIndexById.set(id, i);
|
|
28119
|
+
}
|
|
28120
|
+
};
|
|
28121
|
+
const transcriptHistoryFlags = () => ({
|
|
28122
|
+
transcriptHistoryBefore: transcriptSpill.hasOlder,
|
|
28123
|
+
transcriptHistoryAfter: transcriptSpill.hasNewer
|
|
28124
|
+
});
|
|
28125
|
+
const restoreOlderTranscript = () => {
|
|
28126
|
+
const transcriptViewItems = transcriptSpill.restoreOlder(state.items);
|
|
28127
|
+
if (!transcriptViewItems) return false;
|
|
28128
|
+
set({
|
|
28129
|
+
transcriptViewItems,
|
|
28130
|
+
transcriptViewRevision: state.transcriptViewRevision + 1,
|
|
28131
|
+
...transcriptHistoryFlags()
|
|
28132
|
+
});
|
|
28133
|
+
flushEmitImmediate();
|
|
28134
|
+
return true;
|
|
28135
|
+
};
|
|
28136
|
+
const restoreNewerTranscript = () => {
|
|
28137
|
+
const restored = transcriptSpill.restoreNewer(state.items);
|
|
28138
|
+
if (!restored) return false;
|
|
28139
|
+
set({
|
|
28140
|
+
transcriptViewItems: restored.atLive ? null : restored,
|
|
28141
|
+
transcriptViewRevision: state.transcriptViewRevision + 1,
|
|
28142
|
+
...transcriptHistoryFlags()
|
|
28143
|
+
});
|
|
28144
|
+
flushEmitImmediate();
|
|
28145
|
+
return true;
|
|
28146
|
+
};
|
|
28147
|
+
const replaceItems = (items, {
|
|
28148
|
+
preserveStreamingTail = false,
|
|
28149
|
+
preserveSpill = false,
|
|
28150
|
+
preserveTranscriptView = false
|
|
28151
|
+
} = {}) => {
|
|
27328
28152
|
const nextItems = Array.isArray(items) ? items : [];
|
|
28153
|
+
if (!preserveSpill) transcriptSpill.reset();
|
|
28154
|
+
const liveItems = transcriptSpill.capLive(nextItems);
|
|
28155
|
+
const previousTranscriptView = state.transcriptViewItems;
|
|
28156
|
+
const nextTranscriptView = preserveTranscriptView && previousTranscriptView ? refillTranscriptViewOverlap(previousTranscriptView, state.items, liveItems) : null;
|
|
28157
|
+
const transcriptViewChanged = nextTranscriptView !== previousTranscriptView;
|
|
27329
28158
|
activeToolCalls.clear();
|
|
28159
|
+
const structureRevision = state.structureRevision;
|
|
27330
28160
|
state = replaceEngineItemsState({
|
|
27331
28161
|
state,
|
|
27332
|
-
items:
|
|
28162
|
+
items: liveItems,
|
|
27333
28163
|
itemIndexById,
|
|
27334
28164
|
preserveStreamingTail,
|
|
27335
28165
|
extra: {
|
|
27336
|
-
promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
|
|
27337
|
-
activeToolSummary: null
|
|
28166
|
+
promptHistoryList: preserveSpill ? state.promptHistoryList : buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
|
|
28167
|
+
activeToolSummary: null,
|
|
28168
|
+
transcriptViewItems: nextTranscriptView,
|
|
28169
|
+
transcriptViewRevision: preserveTranscriptView ? state.transcriptViewRevision + (transcriptViewChanged ? 1 : 0) : state.transcriptViewRevision + 1,
|
|
28170
|
+
...transcriptHistoryFlags()
|
|
27338
28171
|
}
|
|
27339
28172
|
});
|
|
28173
|
+
state = { ...state, structureRevision };
|
|
28174
|
+
publisher.markStructureChange();
|
|
27340
28175
|
emit();
|
|
27341
|
-
return
|
|
28176
|
+
return liveItems;
|
|
27342
28177
|
};
|
|
27343
28178
|
const activeToolCalls = /* @__PURE__ */ new Map();
|
|
27344
28179
|
const recomputeActiveToolSummary = () => {
|
|
@@ -27378,16 +28213,30 @@ async function createEngineSession({
|
|
|
27378
28213
|
if (!flags.pushingFromDeferredEntry && flags.flushDeferredBeforeImmediatePush) {
|
|
27379
28214
|
flags.flushDeferredBeforeImmediatePush();
|
|
27380
28215
|
}
|
|
27381
|
-
const
|
|
27382
|
-
const items =
|
|
28216
|
+
const uncappedItems = [...state.items, item];
|
|
28217
|
+
const items = transcriptSpill.capLive(uncappedItems);
|
|
28218
|
+
if (items !== uncappedItems) reindexLiveItems(items);
|
|
28219
|
+
const index = items.length - 1;
|
|
27383
28220
|
if (item?.id != null) itemIndexById.set(item.id, index);
|
|
27384
28221
|
if (item?.kind === "user") {
|
|
27385
28222
|
const promptHistoryList = buildMergedPromptHistory(recomputePromptHistory(items), loadPromptHistory(state.cwd));
|
|
27386
|
-
set({ items, structureRevision: state.structureRevision + 1, promptHistoryList });
|
|
28223
|
+
set({ items, structureRevision: state.structureRevision + 1, promptHistoryList, ...transcriptHistoryFlags() });
|
|
28224
|
+
flushEmitImmediate();
|
|
27387
28225
|
} else {
|
|
27388
|
-
set({ items, structureRevision: state.structureRevision + 1 });
|
|
28226
|
+
set({ items, structureRevision: state.structureRevision + 1, ...transcriptHistoryFlags() });
|
|
27389
28227
|
}
|
|
27390
28228
|
};
|
|
28229
|
+
const appendItems = (newItems, extra = {}) => {
|
|
28230
|
+
if (!Array.isArray(newItems) || newItems.length === 0) return set(extra);
|
|
28231
|
+
const items = transcriptSpill.capLive([...state.items, ...newItems]);
|
|
28232
|
+
reindexLiveItems(items);
|
|
28233
|
+
return set({
|
|
28234
|
+
items,
|
|
28235
|
+
structureRevision: state.structureRevision + 1,
|
|
28236
|
+
...transcriptHistoryFlags(),
|
|
28237
|
+
...extra
|
|
28238
|
+
});
|
|
28239
|
+
};
|
|
27391
28240
|
const updateStreamingTail = (id, patch = {}, extra = {}) => {
|
|
27392
28241
|
if (id == null) return false;
|
|
27393
28242
|
const current = state.streamingTail?.id === id ? state.streamingTail : { kind: "assistant", id, text: "", streaming: true };
|
|
@@ -27412,7 +28261,9 @@ async function createEngineSession({
|
|
|
27412
28261
|
const { patchItem, settleStreamingTail } = createEngineItemMutators({
|
|
27413
28262
|
getState: () => state,
|
|
27414
28263
|
set,
|
|
27415
|
-
itemIndexById
|
|
28264
|
+
itemIndexById,
|
|
28265
|
+
normalizeItems: (items) => transcriptSpill.capLive(items),
|
|
28266
|
+
itemStateExtra: transcriptHistoryFlags
|
|
27416
28267
|
});
|
|
27417
28268
|
const upsertSyntheticToolItem = (text, id = nextId(), parsed = null) => {
|
|
27418
28269
|
const synthetic = parseSyntheticAgentMessage(text);
|
|
@@ -27626,10 +28477,17 @@ async function createEngineSession({
|
|
|
27626
28477
|
listeners,
|
|
27627
28478
|
itemIndexById,
|
|
27628
28479
|
getState: () => state,
|
|
28480
|
+
getPublishedState: () => publishedState,
|
|
27629
28481
|
set,
|
|
28482
|
+
flushEmit,
|
|
28483
|
+
flushEmitImmediate,
|
|
28484
|
+
disposeEmit: publisher.dispose,
|
|
27630
28485
|
pushItem,
|
|
28486
|
+
appendItems,
|
|
27631
28487
|
patchItem,
|
|
27632
28488
|
replaceItems,
|
|
28489
|
+
restoreOlderTranscript,
|
|
28490
|
+
restoreNewerTranscript,
|
|
27633
28491
|
updateStreamingTail,
|
|
27634
28492
|
settleStreamingTail,
|
|
27635
28493
|
clearStreamingTail,
|
|
@@ -27649,6 +28507,10 @@ async function createEngineSession({
|
|
|
27649
28507
|
baseRouteState,
|
|
27650
28508
|
routeState,
|
|
27651
28509
|
syncContextStats,
|
|
28510
|
+
disposeTranscriptSpill: () => transcriptSpill.dispose(),
|
|
28511
|
+
snapshotTranscriptSpill: () => transcriptSpill.snapshot(),
|
|
28512
|
+
restoreTranscriptSpill: (snapshot) => transcriptSpill.restoreSnapshot(snapshot),
|
|
28513
|
+
releaseTranscriptSpill: (snapshot) => transcriptSpill.releaseSnapshot(snapshot),
|
|
27652
28514
|
presentNextToolApproval,
|
|
27653
28515
|
finishToolApproval,
|
|
27654
28516
|
denyAllToolApprovals,
|
|
@@ -27891,7 +28753,7 @@ function scheduleHardExit(code = 0) {
|
|
|
27891
28753
|
timer2.unref?.();
|
|
27892
28754
|
}
|
|
27893
28755
|
function resolveTuiStderrLogPath() {
|
|
27894
|
-
return process.env.MIXDOG_TUI_STDERR_LOG ||
|
|
28756
|
+
return process.env.MIXDOG_TUI_STDERR_LOG || join11(process.env.MIXDOG_RUNTIME_ROOT || join11(tmpdir3(), "mixdog"), "mixdog-tui.stderr.log");
|
|
27895
28757
|
}
|
|
27896
28758
|
function ansiFg(rgb) {
|
|
27897
28759
|
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ""));
|