@quandev104/pi-style 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/extensions/pi-style.js +763 -319
- package/dist/extensions/pi-style.js.map +1 -1
- package/extension-src/pi-style/app/command-service.ts +2 -0
- package/extension-src/pi-style/app/index.ts +3 -2
- package/extension-src/pi-style/app/runtime.ts +72 -84
- package/extension-src/pi-style/app/snapshot.ts +41 -2
- package/extension-src/pi-style/domain/config-normalization.ts +3 -0
- package/extension-src/pi-style/domain/config-types.ts +4 -0
- package/extension-src/pi-style/features/editor/index.ts +80 -61
- package/extension-src/pi-style/features/messages/index.ts +194 -56
- package/extension-src/pi-style/features/tools/bash-execution.ts +12 -1
- package/extension-src/pi-style/features/tools/boxed/bash.ts +99 -31
- package/extension-src/pi-style/features/tools/boxed/batch.ts +44 -10
- package/extension-src/pi-style/features/tools/boxed/edit.ts +30 -15
- package/extension-src/pi-style/features/tools/boxed/index.ts +7 -2
- package/extension-src/pi-style/features/tools/boxed/quick-edit.ts +31 -15
- package/extension-src/pi-style/features/tools/boxed/session-config.ts +48 -14
- package/extension-src/pi-style/features/tools/boxed/shared.ts +25 -0
- package/extension-src/pi-style/features/tools/boxed/turn-summary.ts +56 -11
- package/extension-src/pi-style/features/tools/index.ts +19 -4
- package/extension-src/pi-style/pi/compatibility-probe.ts +1 -1
- package/extension-src/pi-style/pi/index.ts +26 -13
- package/extension-src/pi-style/pi/session-usage.ts +204 -21
- package/extension-src/pi-style/shared/box.ts +43 -8
- package/package.json +1 -1
|
@@ -183,9 +183,9 @@ function rgbTo256(r, g, b) {
|
|
|
183
183
|
function colorEscapes(mode, hex, kind) {
|
|
184
184
|
if (!isHexColor(hex)) return void 0;
|
|
185
185
|
const { r, g, b } = hexToRgb(hex);
|
|
186
|
-
const
|
|
186
|
+
const cacheKey2 = `${mode}:${kind}:${hex}`;
|
|
187
187
|
const cache = kind === "fg" ? fgEscapeCache : bgEscapeCache;
|
|
188
|
-
let cached = cache.get(
|
|
188
|
+
let cached = cache.get(cacheKey2);
|
|
189
189
|
if (!cached) {
|
|
190
190
|
if (mode === "256color") {
|
|
191
191
|
const idx = rgbTo256(r, g, b);
|
|
@@ -199,7 +199,7 @@ function colorEscapes(mode, hex, kind) {
|
|
|
199
199
|
suffix: kind === "fg" ? "\x1B[39m" : RESET_BACKGROUND
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
cache.set(
|
|
202
|
+
cache.set(cacheKey2, cached);
|
|
203
203
|
}
|
|
204
204
|
return cached;
|
|
205
205
|
}
|
|
@@ -411,8 +411,8 @@ function truncatePrintableAscii(text, width, ellipsis = TRUNCATE_ELLIPSIS) {
|
|
|
411
411
|
const targetWidth = Math.max(0, width - ellipsisWidth);
|
|
412
412
|
return { text: `${text.slice(0, targetWidth)}${ellipsis}`, visibleWidth: targetWidth + ellipsisWidth };
|
|
413
413
|
}
|
|
414
|
-
function truncateSgrAscii(text, width, ellipsis,
|
|
415
|
-
if (
|
|
414
|
+
function truncateSgrAscii(text, width, ellipsis, visibleWidth4) {
|
|
415
|
+
if (visibleWidth4 <= width) return { text, visibleWidth: visibleWidth4 };
|
|
416
416
|
const ellipsisWidth = knownEllipsisWidth(ellipsis);
|
|
417
417
|
if (ellipsisWidth === null) return null;
|
|
418
418
|
if (ellipsisWidth >= width) return truncatePrintableAscii(ellipsis, width, "");
|
|
@@ -684,10 +684,10 @@ function setFullTheme(theme, force = false) {
|
|
|
684
684
|
const themeName = resolveThemeName(theme);
|
|
685
685
|
const sourcePath = resolveThemeSourcePath(theme);
|
|
686
686
|
if (!themeName && !sourcePath) return;
|
|
687
|
-
const
|
|
688
|
-
if (!force &&
|
|
687
|
+
const cacheKey2 = sourcePath || themeName;
|
|
688
|
+
if (!force && cacheKey2 === cachedThemeName && (cachedExtras !== null || cachedVars !== null || cachedColors !== null || cachedThemeExport !== null))
|
|
689
689
|
return;
|
|
690
|
-
cachedThemeName =
|
|
690
|
+
cachedThemeName = cacheKey2;
|
|
691
691
|
const result = readThemeDiscoveryFromPath(sourcePath) ?? (themeName ? discoverThemeExtras(themeName) : null);
|
|
692
692
|
cachedExtras = result?.extras ?? null;
|
|
693
693
|
cachedVars = result?.vars ?? null;
|
|
@@ -703,6 +703,15 @@ function getThemeExtra(_theme, key) {
|
|
|
703
703
|
}
|
|
704
704
|
|
|
705
705
|
// extension-src/pi-style/shared/box.ts
|
|
706
|
+
var THEME_CACHE_KEYS = /* @__PURE__ */ new WeakMap();
|
|
707
|
+
var nextThemeCacheKey = 1;
|
|
708
|
+
function themeCacheKey(theme) {
|
|
709
|
+
const cached = THEME_CACHE_KEYS.get(theme);
|
|
710
|
+
if (cached !== void 0) return cached;
|
|
711
|
+
const created = nextThemeCacheKey++;
|
|
712
|
+
THEME_CACHE_KEYS.set(theme, created);
|
|
713
|
+
return created;
|
|
714
|
+
}
|
|
706
715
|
function shortenPath(path) {
|
|
707
716
|
const home = homedir2();
|
|
708
717
|
if (path.startsWith(home)) return `~${path.slice(home.length)}`;
|
|
@@ -868,7 +877,7 @@ function formatBoxedRunningStatus(theme, elapsedMs) {
|
|
|
868
877
|
function dimLine(text) {
|
|
869
878
|
return `\x1B[2m${text}\x1B[22m`;
|
|
870
879
|
}
|
|
871
|
-
function boxFrameText(
|
|
880
|
+
function boxFrameText(_theme, text) {
|
|
872
881
|
return dimLine(text);
|
|
873
882
|
}
|
|
874
883
|
function boxBorder(theme, left, right, width) {
|
|
@@ -949,8 +958,16 @@ function renderBoxedOutputLines(theme, outputLines, width, rawLineBudget = DEFAU
|
|
|
949
958
|
let nextInputIndex = 0;
|
|
950
959
|
let truncated = false;
|
|
951
960
|
for (; nextInputIndex < outputLines.length; nextInputIndex++) {
|
|
952
|
-
const
|
|
953
|
-
|
|
961
|
+
const fragments = (outputLines[nextInputIndex] ?? "").split("\n");
|
|
962
|
+
let headExceeded = false;
|
|
963
|
+
for (const fragment of fragments) {
|
|
964
|
+
const line = boxedTruncatedLine(theme, fragment, width);
|
|
965
|
+
if (!pushBoundedLines(head, [line], headLimit)) {
|
|
966
|
+
headExceeded = true;
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (headExceeded) {
|
|
954
971
|
truncated = true;
|
|
955
972
|
nextInputIndex++;
|
|
956
973
|
break;
|
|
@@ -960,9 +977,12 @@ function renderBoxedOutputLines(theme, outputLines, width, rawLineBudget = DEFAU
|
|
|
960
977
|
const tail = [];
|
|
961
978
|
const tailStart = Math.max(nextInputIndex, outputLines.length - tailLimit);
|
|
962
979
|
for (let i = tailStart; i < outputLines.length; i++) {
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
|
|
980
|
+
const fragments = (outputLines[i] ?? "").split("\n");
|
|
981
|
+
for (const fragment of fragments) {
|
|
982
|
+
const line = boxedTruncatedLine(theme, fragment, width);
|
|
983
|
+
tail.push(line);
|
|
984
|
+
if (tail.length > tailLimit) tail.splice(0, tail.length - tailLimit);
|
|
985
|
+
}
|
|
966
986
|
}
|
|
967
987
|
const skippedInputLines = Math.max(0, tailStart - nextInputIndex);
|
|
968
988
|
const skippedText = skippedInputLines > 0 ? `\u2026 rendered output truncated; ${skippedInputLines} input lines skipped before tail` : "\u2026 rendered output truncated";
|
|
@@ -1083,6 +1103,7 @@ function renderBoxedToolResult(theme, body, options = {}) {
|
|
|
1083
1103
|
const bodyLines = typeof body === "function" ? body(maxContentWidth) : body.render(maxContentWidth);
|
|
1084
1104
|
const errorPrefix = options.isError ? [theme.fg("error", options.errorLabel ?? "\u2717 Error")] : [];
|
|
1085
1105
|
const outputLines = bodyLines.length > 0 ? [...errorPrefix, ...bodyLines] : [theme.fg("muted", `\u2205 ${options.emptyText ?? "(no output)"}`)];
|
|
1106
|
+
const outputFragments = outputLines.flatMap((line) => line.split("\n"));
|
|
1086
1107
|
const footerText = (options.footerLines ?? []).join(" \xB7 ");
|
|
1087
1108
|
const dividerText = typeof options.dividerLabel === "function" ? options.dividerLabel(renderedWidth) : options.dividerLabel ?? "Response";
|
|
1088
1109
|
const rendered = [
|
|
@@ -1097,7 +1118,12 @@ function renderBoxedToolResult(theme, body, options = {}) {
|
|
|
1097
1118
|
)
|
|
1098
1119
|
],
|
|
1099
1120
|
boxBlankLine(theme, renderedWidth),
|
|
1100
|
-
...renderBoxedOutputLines(
|
|
1121
|
+
...renderBoxedOutputLines(
|
|
1122
|
+
theme,
|
|
1123
|
+
outputFragments,
|
|
1124
|
+
renderedWidth,
|
|
1125
|
+
options.renderLineBudget ?? outputFragments.length
|
|
1126
|
+
),
|
|
1101
1127
|
boxBlankLine(theme, renderedWidth),
|
|
1102
1128
|
boxLabeledBorder(
|
|
1103
1129
|
theme,
|
|
@@ -1419,7 +1445,8 @@ var sessionToolsConfig = {
|
|
|
1419
1445
|
showElapsed: true,
|
|
1420
1446
|
batchOpenGlyph: "\u25CF",
|
|
1421
1447
|
nerdFonts: false,
|
|
1422
|
-
collapseAfterTurn: true
|
|
1448
|
+
collapseAfterTurn: true,
|
|
1449
|
+
collapseMutatingTools: false
|
|
1423
1450
|
};
|
|
1424
1451
|
function setToolsRenderConfig(config) {
|
|
1425
1452
|
sessionToolsConfig = { ...sessionToolsConfig, ...config };
|
|
@@ -1427,6 +1454,18 @@ function setToolsRenderConfig(config) {
|
|
|
1427
1454
|
function getToolsRenderConfig() {
|
|
1428
1455
|
return sessionToolsConfig;
|
|
1429
1456
|
}
|
|
1457
|
+
function getToolsRenderCacheSignature() {
|
|
1458
|
+
return [
|
|
1459
|
+
sessionToolsConfig.maxCollapsedLines,
|
|
1460
|
+
sessionToolsConfig.maxExpandedLines,
|
|
1461
|
+
sessionToolsConfig.dimOutput ? 1 : 0,
|
|
1462
|
+
sessionToolsConfig.showElapsed ? 1 : 0,
|
|
1463
|
+
sessionToolsConfig.batchOpenGlyph,
|
|
1464
|
+
sessionToolsConfig.nerdFonts ? 1 : 0,
|
|
1465
|
+
sessionToolsConfig.collapseAfterTurn ? 1 : 0,
|
|
1466
|
+
sessionToolsConfig.collapseMutatingTools ? 1 : 0
|
|
1467
|
+
].join("|");
|
|
1468
|
+
}
|
|
1430
1469
|
var STARTED_AT_KEY = "__piStyleStartedAt";
|
|
1431
1470
|
var ENDED_AT_KEY = "__piStyleEndedAt";
|
|
1432
1471
|
var RESULT_SEEN_KEY = "__piStyleResultSeen";
|
|
@@ -1454,27 +1493,35 @@ function markResultSeen(state) {
|
|
|
1454
1493
|
if (!state || typeof state !== "object") return;
|
|
1455
1494
|
state[RESULT_SEEN_KEY] = true;
|
|
1456
1495
|
}
|
|
1457
|
-
var
|
|
1496
|
+
var tickerEntries = /* @__PURE__ */ new Map();
|
|
1497
|
+
var sharedTickerHandle;
|
|
1498
|
+
function ensureSharedTicker() {
|
|
1499
|
+
if (sharedTickerHandle !== void 0 || tickerEntries.size === 0) return;
|
|
1500
|
+
sharedTickerHandle = setInterval(() => {
|
|
1501
|
+
for (const { invalidate } of tickerEntries.values()) invalidate();
|
|
1502
|
+
}, 1e3);
|
|
1503
|
+
}
|
|
1504
|
+
function stopSharedTickerIfIdle() {
|
|
1505
|
+
if (sharedTickerHandle === void 0 || tickerEntries.size > 0) return;
|
|
1506
|
+
clearInterval(sharedTickerHandle);
|
|
1507
|
+
sharedTickerHandle = void 0;
|
|
1508
|
+
}
|
|
1458
1509
|
function startElapsedTicker(state, invalidate) {
|
|
1459
1510
|
if (!state || typeof state !== "object") return;
|
|
1460
|
-
|
|
1461
|
-
state
|
|
1462
|
-
|
|
1511
|
+
state[TICKER_KEY] = true;
|
|
1512
|
+
tickerEntries.set(state, { invalidate });
|
|
1513
|
+
ensureSharedTicker();
|
|
1463
1514
|
}
|
|
1464
1515
|
function stopElapsedTicker(state) {
|
|
1465
1516
|
if (!state || typeof state !== "object") return;
|
|
1466
|
-
const handle = state[TICKER_KEY];
|
|
1467
|
-
if (handle !== void 0) clearInterval(handle);
|
|
1468
1517
|
delete state[TICKER_KEY];
|
|
1469
|
-
|
|
1518
|
+
tickerEntries.delete(state);
|
|
1519
|
+
stopSharedTickerIfIdle();
|
|
1470
1520
|
}
|
|
1471
1521
|
function stopAllElapsedTickers() {
|
|
1472
|
-
for (const state of
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
delete state[TICKER_KEY];
|
|
1476
|
-
}
|
|
1477
|
-
tickerStates.clear();
|
|
1522
|
+
for (const state of tickerEntries.keys()) delete state[TICKER_KEY];
|
|
1523
|
+
tickerEntries.clear();
|
|
1524
|
+
stopSharedTickerIfIdle();
|
|
1478
1525
|
}
|
|
1479
1526
|
|
|
1480
1527
|
// extension-src/pi-style/features/tools/boxed/batch.ts
|
|
@@ -1510,6 +1557,7 @@ function createBatch(meta, leaderId, detail, opts = {}) {
|
|
|
1510
1557
|
leaderId,
|
|
1511
1558
|
startedAt: performance.now(),
|
|
1512
1559
|
closed: false,
|
|
1560
|
+
revision: 0,
|
|
1513
1561
|
members: [
|
|
1514
1562
|
{
|
|
1515
1563
|
toolCallId: leaderId,
|
|
@@ -1525,14 +1573,20 @@ function createBatch(meta, leaderId, detail, opts = {}) {
|
|
|
1525
1573
|
batchByCallId.set(leaderId, batch);
|
|
1526
1574
|
return batch;
|
|
1527
1575
|
}
|
|
1576
|
+
function bumpBatchRevision(batch) {
|
|
1577
|
+
batch.revision++;
|
|
1578
|
+
delete batch.renderCache;
|
|
1579
|
+
}
|
|
1528
1580
|
function registerBatchCall(meta, detail, context, opts = {}) {
|
|
1529
1581
|
const existing = batchByCallId.get(context.toolCallId);
|
|
1530
1582
|
if (existing) {
|
|
1531
1583
|
const member2 = existing.members.find((entry) => entry.toolCallId === context.toolCallId);
|
|
1532
1584
|
if (member2) {
|
|
1585
|
+
const changed = member2.detail !== detail || member2.pattern !== opts.pattern || member2.pathLabel !== opts.pathLabel;
|
|
1533
1586
|
member2.detail = detail;
|
|
1534
1587
|
if (opts.pattern !== void 0) member2.pattern = opts.pattern;
|
|
1535
1588
|
if (opts.pathLabel !== void 0) member2.pathLabel = opts.pathLabel;
|
|
1589
|
+
if (changed) bumpBatchRevision(existing);
|
|
1536
1590
|
}
|
|
1537
1591
|
return { batch: existing, isLeader: existing.leaderId === context.toolCallId };
|
|
1538
1592
|
}
|
|
@@ -1551,6 +1605,7 @@ function registerBatchCall(meta, detail, context, opts = {}) {
|
|
|
1551
1605
|
};
|
|
1552
1606
|
current.members.push(member);
|
|
1553
1607
|
batchByCallId.set(context.toolCallId, current);
|
|
1608
|
+
bumpBatchRevision(current);
|
|
1554
1609
|
return { batch: current, isLeader: false };
|
|
1555
1610
|
}
|
|
1556
1611
|
function registerBatchResult(meta, data, context) {
|
|
@@ -1558,14 +1613,19 @@ function registerBatchResult(meta, data, context) {
|
|
|
1558
1613
|
if (!batch || batch.meta.toolName !== meta.toolName) return { batch: void 0, isLeader: false };
|
|
1559
1614
|
const member = batch.members.find((entry) => entry.toolCallId === context.toolCallId);
|
|
1560
1615
|
if (member) {
|
|
1561
|
-
|
|
1562
|
-
|
|
1616
|
+
const nextStatus = data.isPartial ? "running" : "done";
|
|
1617
|
+
const nextIsError = !data.isPartial && data.isError;
|
|
1618
|
+
const changed = member.status !== nextStatus || member.isError !== nextIsError || member.errorText !== (nextIsError ? data.errorText : void 0) || member.outputEntries !== data.entries;
|
|
1619
|
+
member.status = nextStatus;
|
|
1620
|
+
member.isError = nextIsError;
|
|
1563
1621
|
if (member.isError && data.errorText !== void 0) member.errorText = data.errorText;
|
|
1564
1622
|
else delete member.errorText;
|
|
1565
1623
|
if (data.entries !== void 0) member.outputEntries = data.entries;
|
|
1624
|
+
if (changed) bumpBatchRevision(batch);
|
|
1566
1625
|
}
|
|
1567
1626
|
if (batch.completedAt === void 0 && batch.members.every((entry) => entry.status === "done")) {
|
|
1568
1627
|
batch.completedAt = performance.now();
|
|
1628
|
+
bumpBatchRevision(batch);
|
|
1569
1629
|
}
|
|
1570
1630
|
return { batch, isLeader: batch.leaderId === context.toolCallId };
|
|
1571
1631
|
}
|
|
@@ -1758,9 +1818,16 @@ function renderBatchPanelLines(theme, batch, status, width) {
|
|
|
1758
1818
|
function renderBatchAwareCall(theme, batch) {
|
|
1759
1819
|
return {
|
|
1760
1820
|
invalidate() {
|
|
1821
|
+
delete batch.renderCache;
|
|
1761
1822
|
},
|
|
1762
1823
|
render(width) {
|
|
1763
|
-
|
|
1824
|
+
const status = batchStatus(batch);
|
|
1825
|
+
if (!status.allDone) return renderBatchPanelLines(theme, batch, status, width);
|
|
1826
|
+
const cacheKey2 = [themeCacheKey(theme), getToolsRenderCacheSignature(), width, batch.revision].join("|");
|
|
1827
|
+
if (batch.renderCache?.key === cacheKey2) return batch.renderCache.lines;
|
|
1828
|
+
const lines = renderBatchPanelLines(theme, batch, status, width);
|
|
1829
|
+
batch.renderCache = { key: cacheKey2, lines };
|
|
1830
|
+
return lines;
|
|
1764
1831
|
}
|
|
1765
1832
|
};
|
|
1766
1833
|
}
|
|
@@ -1775,6 +1842,13 @@ function emptyBatchResult() {
|
|
|
1775
1842
|
}
|
|
1776
1843
|
|
|
1777
1844
|
// extension-src/pi-style/features/tools/boxed/turn-summary.ts
|
|
1845
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "quick_edit", "substitute_edit", "target_edit"]);
|
|
1846
|
+
function isMutatingTool(toolName) {
|
|
1847
|
+
return MUTATING_TOOLS.has(toolName);
|
|
1848
|
+
}
|
|
1849
|
+
function mutatingCollapses() {
|
|
1850
|
+
return getToolsRenderConfig().collapseMutatingTools;
|
|
1851
|
+
}
|
|
1778
1852
|
var memberByCallId = /* @__PURE__ */ new Map();
|
|
1779
1853
|
var invalidateByCallId = /* @__PURE__ */ new Map();
|
|
1780
1854
|
function resetTurnRegistry() {
|
|
@@ -1805,7 +1879,7 @@ function registerTurn(calls, isErrorById, ended) {
|
|
|
1805
1879
|
isError: isErrorById.get(toolCallId) === true
|
|
1806
1880
|
};
|
|
1807
1881
|
});
|
|
1808
|
-
const leader = members.find((member) => !member.isError);
|
|
1882
|
+
const leader = members.find((member) => !member.isError && (!isMutatingTool(member.toolName) || mutatingCollapses()));
|
|
1809
1883
|
const turn = {
|
|
1810
1884
|
leaderId: leader?.toolCallId ?? "",
|
|
1811
1885
|
ended: ended && complete,
|
|
@@ -1835,7 +1909,9 @@ function registerTurnFromMessage(message, toolResults) {
|
|
|
1835
1909
|
isError: isErrorById.get(toolCallId) === true
|
|
1836
1910
|
};
|
|
1837
1911
|
});
|
|
1838
|
-
const leader = newMembers.find(
|
|
1912
|
+
const leader = newMembers.find(
|
|
1913
|
+
(member) => !member.isError && (!isMutatingTool(member.toolName) || mutatingCollapses())
|
|
1914
|
+
);
|
|
1839
1915
|
if (!currentRun) {
|
|
1840
1916
|
currentRun = {
|
|
1841
1917
|
leaderId: leader?.toolCallId ?? "",
|
|
@@ -1935,11 +2011,13 @@ function turnSummaryParts(turn) {
|
|
|
1935
2011
|
const order = [];
|
|
1936
2012
|
let failedCount = 0;
|
|
1937
2013
|
let elapsedMs;
|
|
2014
|
+
const collapseMutating = mutatingCollapses();
|
|
1938
2015
|
for (const member of turn.members) {
|
|
1939
2016
|
if (member.isError) {
|
|
1940
2017
|
failedCount++;
|
|
1941
2018
|
continue;
|
|
1942
2019
|
}
|
|
2020
|
+
if (!collapseMutating && isMutatingTool(member.toolName)) continue;
|
|
1943
2021
|
if (member.elapsedMs !== void 0) elapsedMs = (elapsedMs ?? 0) + member.elapsedMs;
|
|
1944
2022
|
const existing = counts.get(member.toolName);
|
|
1945
2023
|
if (existing === void 0) {
|
|
@@ -1949,8 +2027,8 @@ function turnSummaryParts(turn) {
|
|
|
1949
2027
|
}
|
|
1950
2028
|
const parts = order.map((toolName) => {
|
|
1951
2029
|
const count = counts.get(toolName) ?? 0;
|
|
1952
|
-
const style = TURN_SUMMARY_STYLE[toolName]
|
|
1953
|
-
return `${style.verb} ${count} ${pluralForm(style.unit, count)}`;
|
|
2030
|
+
const style = TURN_SUMMARY_STYLE[toolName];
|
|
2031
|
+
return style ? `${style.verb} ${count} ${pluralForm(style.unit, count)}` : `used ${count} ${toolName}`;
|
|
1954
2032
|
});
|
|
1955
2033
|
return { parts, failedCount, elapsedMs };
|
|
1956
2034
|
}
|
|
@@ -1959,7 +2037,7 @@ function formatTurnSummaryLine(theme, turn) {
|
|
|
1959
2037
|
const parts = summary.parts.join(", ");
|
|
1960
2038
|
let line = `${theme.fg("dim", `\u2794 ${parts}`)}`;
|
|
1961
2039
|
if (summary.failedCount > 0)
|
|
1962
|
-
line += theme.fg("error", ` \xB7 ${summary.failedCount} ${pluralForm("
|
|
2040
|
+
line += theme.fg("error", ` \xB7 ${summary.failedCount} ${pluralForm("failure", summary.failedCount)}`);
|
|
1963
2041
|
if (summary.elapsedMs !== void 0) line += theme.fg("dim", ` \xB7 ${(summary.elapsedMs / 1e3).toFixed(2)}s`);
|
|
1964
2042
|
return line;
|
|
1965
2043
|
}
|
|
@@ -4974,6 +5052,17 @@ function compactFooterWithState(theme, result, context, options = {}) {
|
|
|
4974
5052
|
function resultFooterLines(theme, result, context, extraParts = []) {
|
|
4975
5053
|
return [formatBoxedFooter(theme, result, extraParts, stateElapsedMs(context))];
|
|
4976
5054
|
}
|
|
5055
|
+
function getRenderCacheKey(prefix, theme, ...parts) {
|
|
5056
|
+
return [prefix, themeCacheKey(theme), getToolsRenderCacheSignature(), ...parts].join("|");
|
|
5057
|
+
}
|
|
5058
|
+
function memoizedStateComponent(state, slot, key, build) {
|
|
5059
|
+
if (!state || typeof state !== "object") return build();
|
|
5060
|
+
const cached = state[slot];
|
|
5061
|
+
if (cached && cached.key === key) return cached.component;
|
|
5062
|
+
const component = build();
|
|
5063
|
+
state[slot] = { key, component };
|
|
5064
|
+
return component;
|
|
5065
|
+
}
|
|
4977
5066
|
function clearFooterState(context) {
|
|
4978
5067
|
clearCompactBoxedFooter(context.state);
|
|
4979
5068
|
}
|
|
@@ -5293,11 +5382,11 @@ var EMPTY_BASH_RESULT = Object.freeze({
|
|
|
5293
5382
|
}
|
|
5294
5383
|
});
|
|
5295
5384
|
function createBashResultPreview(theme, text, options, color) {
|
|
5296
|
-
let
|
|
5385
|
+
let cacheKey2 = "";
|
|
5297
5386
|
let cacheLines = null;
|
|
5298
5387
|
return {
|
|
5299
5388
|
invalidate() {
|
|
5300
|
-
|
|
5389
|
+
cacheKey2 = "";
|
|
5301
5390
|
cacheLines = null;
|
|
5302
5391
|
},
|
|
5303
5392
|
render(width) {
|
|
@@ -5305,7 +5394,7 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5305
5394
|
const cfg = getToolsRenderConfig();
|
|
5306
5395
|
const expanded = Boolean(options.expanded);
|
|
5307
5396
|
const cacheId = `${bodyWidth}|${expanded ? 1 : 0}|${cfg.maxExpandedLines}|${cfg.dimOutput ? 1 : 0}`;
|
|
5308
|
-
if (cacheLines &&
|
|
5397
|
+
if (cacheLines && cacheKey2 === cacheId) return cacheLines;
|
|
5309
5398
|
if (!expanded) {
|
|
5310
5399
|
const needed = cfg.maxCollapsedLines;
|
|
5311
5400
|
let totalNewlines = 0;
|
|
@@ -5320,14 +5409,14 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5320
5409
|
}
|
|
5321
5410
|
}
|
|
5322
5411
|
if (text.length === 0) {
|
|
5323
|
-
|
|
5412
|
+
cacheKey2 = cacheId;
|
|
5324
5413
|
cacheLines = [];
|
|
5325
5414
|
return cacheLines;
|
|
5326
5415
|
}
|
|
5327
5416
|
const tail = replaceTabs(text.slice(scanFrom)).replace(/\r/g, "");
|
|
5328
5417
|
const shownLines = tail ? tail.split("\n").map((l) => clampLineLength(l)) : [];
|
|
5329
5418
|
if (shownLines.length === 0) {
|
|
5330
|
-
|
|
5419
|
+
cacheKey2 = cacheId;
|
|
5331
5420
|
cacheLines = [];
|
|
5332
5421
|
return cacheLines;
|
|
5333
5422
|
}
|
|
@@ -5336,7 +5425,7 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5336
5425
|
if (color === "error") return formatToolOutputLine(theme, truncated, "error");
|
|
5337
5426
|
return cfg.dimOutput ? formatToolOutputLine(theme, truncated) : formatToolOutputLine(theme, truncated, "text");
|
|
5338
5427
|
});
|
|
5339
|
-
|
|
5428
|
+
cacheKey2 = cacheId;
|
|
5340
5429
|
cacheLines = truncatedShown;
|
|
5341
5430
|
return cacheLines;
|
|
5342
5431
|
}
|
|
@@ -5344,7 +5433,7 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5344
5433
|
const logicalLines = normalized.split("\n").map((l) => clampLineLength(l));
|
|
5345
5434
|
const hasOutput = !(logicalLines.length === 1 && logicalLines[0] === "");
|
|
5346
5435
|
if (!hasOutput) {
|
|
5347
|
-
|
|
5436
|
+
cacheKey2 = cacheId;
|
|
5348
5437
|
cacheLines = [];
|
|
5349
5438
|
return cacheLines;
|
|
5350
5439
|
}
|
|
@@ -5355,11 +5444,11 @@ function createBashResultPreview(theme, text, options, color) {
|
|
|
5355
5444
|
const truncated = expandedLines.slice(-cfg.maxExpandedLines).map(applyColor);
|
|
5356
5445
|
const remaining = expandedLines.length - cfg.maxExpandedLines;
|
|
5357
5446
|
truncated.unshift(theme.fg("dim", `\u2026 ${remaining} earlier lines`));
|
|
5358
|
-
|
|
5447
|
+
cacheKey2 = cacheId;
|
|
5359
5448
|
cacheLines = truncated;
|
|
5360
5449
|
return cacheLines;
|
|
5361
5450
|
}
|
|
5362
|
-
|
|
5451
|
+
cacheKey2 = cacheId;
|
|
5363
5452
|
cacheLines = expandedLines.map(applyColor);
|
|
5364
5453
|
return cacheLines;
|
|
5365
5454
|
}
|
|
@@ -5573,27 +5662,35 @@ function renderSemanticPanel(theme, toolCallId, context) {
|
|
|
5573
5662
|
},
|
|
5574
5663
|
render(width) {
|
|
5575
5664
|
if (!state) return [];
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5665
|
+
const renderFresh = () => {
|
|
5666
|
+
if (state.fallback) {
|
|
5667
|
+
return renderBoxedBashCall(
|
|
5668
|
+
theme,
|
|
5669
|
+
state.command.split("\n"),
|
|
5670
|
+
context,
|
|
5671
|
+
bashWidthKey(state.command, context?.args?.timeout)
|
|
5672
|
+
).render(width);
|
|
5673
|
+
}
|
|
5674
|
+
if (isBashTreeClass(state.cls)) {
|
|
5675
|
+
const treeState = { cls: state.cls };
|
|
5676
|
+
if (state.parsed !== void 0) treeState.parsed = state.parsed;
|
|
5677
|
+
return renderBashTreeLines(theme, treeState, width);
|
|
5678
|
+
}
|
|
5679
|
+
if (isGhClass(state.cls)) {
|
|
5680
|
+
const ghState = { cls: state.cls };
|
|
5681
|
+
if (state.parsed !== void 0) ghState.parsed = state.parsed;
|
|
5682
|
+
return renderGhCardLines(theme, ghState, width);
|
|
5683
|
+
}
|
|
5684
|
+
const gitState = { cls: state.cls };
|
|
5685
|
+
if (state.parsed !== void 0) gitState.parsed = state.parsed;
|
|
5686
|
+
return renderGitCardLines(theme, gitState, width);
|
|
5687
|
+
};
|
|
5688
|
+
if (!state.finished) return renderFresh();
|
|
5689
|
+
const cacheKey2 = [themeCacheKey(theme), getToolsRenderCacheSignature(), width, state.revision].join("|");
|
|
5690
|
+
if (state.renderCache?.key === cacheKey2) return state.renderCache.lines;
|
|
5691
|
+
const lines = renderFresh();
|
|
5692
|
+
state.renderCache = { key: cacheKey2, lines };
|
|
5693
|
+
return lines;
|
|
5597
5694
|
}
|
|
5598
5695
|
};
|
|
5599
5696
|
}
|
|
@@ -5602,7 +5699,26 @@ var bashTool = {
|
|
|
5602
5699
|
noteExecutionStart(context);
|
|
5603
5700
|
const cls = classifyBashSemantic(String(args?.command ?? ""));
|
|
5604
5701
|
if (cls) {
|
|
5605
|
-
|
|
5702
|
+
const command = String(args?.command ?? "");
|
|
5703
|
+
const existing = semanticStates.get(context.toolCallId);
|
|
5704
|
+
if (existing) {
|
|
5705
|
+
if (existing.command !== command || existing.cls.kind !== cls.kind) {
|
|
5706
|
+
delete existing.parsed;
|
|
5707
|
+
delete existing.fallback;
|
|
5708
|
+
existing.finished = false;
|
|
5709
|
+
existing.revision++;
|
|
5710
|
+
delete existing.renderCache;
|
|
5711
|
+
}
|
|
5712
|
+
existing.command = command;
|
|
5713
|
+
existing.cls = cls;
|
|
5714
|
+
} else {
|
|
5715
|
+
semanticStates.set(context.toolCallId, {
|
|
5716
|
+
cls,
|
|
5717
|
+
command,
|
|
5718
|
+
finished: false,
|
|
5719
|
+
revision: 0
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
5606
5722
|
return renderSemanticPanel(theme, context.toolCallId, context);
|
|
5607
5723
|
}
|
|
5608
5724
|
noteBoxedCallState(context);
|
|
@@ -5623,12 +5739,18 @@ var bashTool = {
|
|
|
5623
5739
|
const parsed = parseSemanticOutput(cls, output);
|
|
5624
5740
|
const state = semanticStates.get(context.toolCallId);
|
|
5625
5741
|
if (parsed) {
|
|
5626
|
-
if (state)
|
|
5627
|
-
|
|
5742
|
+
if (state) {
|
|
5743
|
+
state.parsed = parsed;
|
|
5744
|
+
state.finished = !options.isPartial;
|
|
5745
|
+
state.revision++;
|
|
5746
|
+
delete state.renderCache;
|
|
5747
|
+
} else
|
|
5628
5748
|
semanticStates.set(context.toolCallId, {
|
|
5629
5749
|
cls,
|
|
5630
5750
|
command: String(context?.args?.command ?? ""),
|
|
5631
|
-
parsed
|
|
5751
|
+
parsed,
|
|
5752
|
+
finished: !options.isPartial,
|
|
5753
|
+
revision: 0
|
|
5632
5754
|
});
|
|
5633
5755
|
if (isGitDiffClass(cls)) {
|
|
5634
5756
|
return renderGitDiffResult(theme, parsed, options, context);
|
|
@@ -5638,7 +5760,12 @@ var bashTool = {
|
|
|
5638
5760
|
}
|
|
5639
5761
|
return EMPTY_BASH_TREE_RESULT;
|
|
5640
5762
|
}
|
|
5641
|
-
if (state)
|
|
5763
|
+
if (state) {
|
|
5764
|
+
state.fallback = true;
|
|
5765
|
+
state.finished = !options.isPartial;
|
|
5766
|
+
state.revision++;
|
|
5767
|
+
delete state.renderCache;
|
|
5768
|
+
}
|
|
5642
5769
|
}
|
|
5643
5770
|
} else if (options.isPartial) {
|
|
5644
5771
|
startElapsedTicker(context.state, context.invalidate);
|
|
@@ -5651,7 +5778,20 @@ var bashTool = {
|
|
|
5651
5778
|
if (firstResultPass) return EMPTY_BASH_RESULT;
|
|
5652
5779
|
return renderBashStreamingResult(theme, raw, options, context);
|
|
5653
5780
|
}
|
|
5654
|
-
return
|
|
5781
|
+
return memoizedStateComponent(
|
|
5782
|
+
context.state,
|
|
5783
|
+
"__piStyleBashFinalResult",
|
|
5784
|
+
getRenderCacheKey(
|
|
5785
|
+
"bash-final-result",
|
|
5786
|
+
theme,
|
|
5787
|
+
Boolean(options.expanded),
|
|
5788
|
+
Boolean(context.isError),
|
|
5789
|
+
String(context?.args?.command ?? ""),
|
|
5790
|
+
raw,
|
|
5791
|
+
getStateElapsedMs(context.state) ?? ""
|
|
5792
|
+
),
|
|
5793
|
+
() => renderBashFinalResult(theme, raw, options, context)
|
|
5794
|
+
);
|
|
5655
5795
|
}
|
|
5656
5796
|
};
|
|
5657
5797
|
|
|
@@ -5731,21 +5871,33 @@ var editTool = {
|
|
|
5731
5871
|
const maxRows = expanded ? 160 : 36;
|
|
5732
5872
|
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
5733
5873
|
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
5734
|
-
return
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5874
|
+
return memoizedStateComponent(
|
|
5875
|
+
context.state,
|
|
5876
|
+
"__piStyleEditDiffResult",
|
|
5877
|
+
getRenderCacheKey(
|
|
5878
|
+
"edit-diff-result",
|
|
5879
|
+
theme,
|
|
5880
|
+
Boolean(expanded),
|
|
5881
|
+
diff,
|
|
5882
|
+
sourcePath ?? "",
|
|
5883
|
+
editDiffFooter(theme, result, context, stats)
|
|
5884
|
+
),
|
|
5885
|
+
() => renderBoxedToolResult(
|
|
5886
|
+
theme,
|
|
5887
|
+
{
|
|
5888
|
+
render(width) {
|
|
5889
|
+
return diffView.render(width);
|
|
5890
|
+
},
|
|
5891
|
+
invalidate() {
|
|
5892
|
+
diffView.invalidate();
|
|
5893
|
+
}
|
|
5739
5894
|
},
|
|
5740
|
-
|
|
5741
|
-
|
|
5895
|
+
{
|
|
5896
|
+
dividerLabel: diffDividerLabel2(theme, stats),
|
|
5897
|
+
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
5898
|
+
footerLines: [editDiffFooter(theme, result, context, stats)]
|
|
5742
5899
|
}
|
|
5743
|
-
|
|
5744
|
-
{
|
|
5745
|
-
dividerLabel: diffDividerLabel2(theme, stats),
|
|
5746
|
-
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
5747
|
-
footerLines: [editDiffFooter(theme, result, context, stats)]
|
|
5748
|
-
}
|
|
5900
|
+
)
|
|
5749
5901
|
);
|
|
5750
5902
|
}
|
|
5751
5903
|
};
|
|
@@ -6142,21 +6294,34 @@ function renderQuickEditResult(_toolName, result, options, theme, context, confi
|
|
|
6142
6294
|
const maxRows = expanded ? 160 : 36;
|
|
6143
6295
|
const diffView = new AdaptiveDiffComponent(theme, rows, maxRows, shouldHighlight ? language : void 0);
|
|
6144
6296
|
const expandHint2 = !expanded && diffView.hasCollapsed() ? "Ctrl+O more" : void 0;
|
|
6145
|
-
return
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6297
|
+
return memoizedStateComponent(
|
|
6298
|
+
context.state,
|
|
6299
|
+
"__piStyleQuickEditDiffResult",
|
|
6300
|
+
getRenderCacheKey(
|
|
6301
|
+
"quick-edit-diff-result",
|
|
6302
|
+
theme,
|
|
6303
|
+
config.toolLabel,
|
|
6304
|
+
Boolean(expanded),
|
|
6305
|
+
diff,
|
|
6306
|
+
argPath,
|
|
6307
|
+
quickEditDiffFooter(theme, result, context, stats)
|
|
6308
|
+
),
|
|
6309
|
+
() => renderBoxedToolResult(
|
|
6310
|
+
theme,
|
|
6311
|
+
{
|
|
6312
|
+
render(width) {
|
|
6313
|
+
return diffView.render(width);
|
|
6314
|
+
},
|
|
6315
|
+
invalidate() {
|
|
6316
|
+
diffView.invalidate();
|
|
6317
|
+
}
|
|
6150
6318
|
},
|
|
6151
|
-
|
|
6152
|
-
|
|
6319
|
+
{
|
|
6320
|
+
dividerLabel: quickEditDividerLabel(theme, stats),
|
|
6321
|
+
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
6322
|
+
footerLines: [quickEditDiffFooter(theme, result, context, stats)]
|
|
6153
6323
|
}
|
|
6154
|
-
|
|
6155
|
-
{
|
|
6156
|
-
dividerLabel: quickEditDividerLabel(theme, stats),
|
|
6157
|
-
...expandHint2 ? { dividerRightLabel: expandHint2 } : {},
|
|
6158
|
-
footerLines: [quickEditDiffFooter(theme, result, context, stats)]
|
|
6159
|
-
}
|
|
6324
|
+
)
|
|
6160
6325
|
);
|
|
6161
6326
|
}
|
|
6162
6327
|
function quickEditFooter(theme, context) {
|
|
@@ -6314,9 +6479,11 @@ var REGISTRY = {
|
|
|
6314
6479
|
target_edit: quickEditToolFor("target_edit")
|
|
6315
6480
|
};
|
|
6316
6481
|
function collapsedTurnFor(toolCallId, expanded) {
|
|
6317
|
-
|
|
6482
|
+
const config = getToolsRenderConfig();
|
|
6483
|
+
if (expanded || !config.collapseAfterTurn) return void 0;
|
|
6318
6484
|
const entry = getTurnEntry(toolCallId);
|
|
6319
6485
|
if (!entry?.turn.ended || entry.member.isError) return void 0;
|
|
6486
|
+
if (isMutatingTool(entry.member.toolName) && !config.collapseMutatingTools) return void 0;
|
|
6320
6487
|
return entry.turn;
|
|
6321
6488
|
}
|
|
6322
6489
|
function renderBoxedToolCall2(toolName, args, theme, context) {
|
|
@@ -6596,14 +6763,27 @@ function createToolDecorationOwner(snapshot = {}) {
|
|
|
6596
6763
|
if (typeof renderer !== "function") {
|
|
6597
6764
|
neutralizeToolContainerBackground(instance);
|
|
6598
6765
|
if (subtype === "tool-call-renderer")
|
|
6599
|
-
return (callArgs, theme, context) =>
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6766
|
+
return (callArgs, theme, context) => {
|
|
6767
|
+
const component = renderBoxedToolCall2(
|
|
6768
|
+
toolName,
|
|
6769
|
+
callArgs,
|
|
6770
|
+
theme,
|
|
6771
|
+
context
|
|
6772
|
+
);
|
|
6773
|
+
if (component === EMPTY_BATCH_COMPONENT) hideBatchMember(instance);
|
|
6774
|
+
return component;
|
|
6775
|
+
};
|
|
6776
|
+
return (result, options, theme, context) => {
|
|
6777
|
+
const component = renderBoxedToolResult2(
|
|
6778
|
+
toolName,
|
|
6779
|
+
result,
|
|
6780
|
+
options,
|
|
6781
|
+
theme,
|
|
6782
|
+
context
|
|
6783
|
+
);
|
|
6784
|
+
if (component === EMPTY_BATCH_COMPONENT) hideBatchMember(instance);
|
|
6785
|
+
return component;
|
|
6786
|
+
};
|
|
6607
6787
|
}
|
|
6608
6788
|
return function(...rendererArgs) {
|
|
6609
6789
|
const valid = subtype === "tool-call-renderer" ? validCallArgs(rendererArgs) : validResultArgs(rendererArgs);
|
|
@@ -6824,7 +7004,8 @@ var DEFAULT_CONFIG = Object.freeze({
|
|
|
6824
7004
|
maxExpandedLines: 50,
|
|
6825
7005
|
dimOutput: false,
|
|
6826
7006
|
showElapsed: true,
|
|
6827
|
-
collapseAfterTurn: true
|
|
7007
|
+
collapseAfterTurn: true,
|
|
7008
|
+
collapseMutatingTools: false
|
|
6828
7009
|
}),
|
|
6829
7010
|
theme: Object.freeze({
|
|
6830
7011
|
nerdFonts: "auto",
|
|
@@ -6940,7 +7121,8 @@ function normalizeConfig(input, defaults = DEFAULT_CONFIG) {
|
|
|
6940
7121
|
maxExpandedLines: maxExpanded,
|
|
6941
7122
|
dimOutput: bool(tools.dimOutput, defaults.tools.dimOutput),
|
|
6942
7123
|
showElapsed: bool(tools.showElapsed, defaults.tools.showElapsed),
|
|
6943
|
-
collapseAfterTurn: bool(tools.collapseAfterTurn, defaults.tools.collapseAfterTurn)
|
|
7124
|
+
collapseAfterTurn: bool(tools.collapseAfterTurn, defaults.tools.collapseAfterTurn),
|
|
7125
|
+
collapseMutatingTools: bool(tools.collapseMutatingTools, defaults.tools.collapseMutatingTools)
|
|
6944
7126
|
}),
|
|
6945
7127
|
theme: Object.freeze({
|
|
6946
7128
|
nerdFonts: stringEnum(theme.nerdFonts, ["auto", "on", "off"], defaults.theme.nerdFonts),
|
|
@@ -6988,6 +7170,7 @@ var BOOL_PATHS = /* @__PURE__ */ new Set([
|
|
|
6988
7170
|
"tools.showElapsed",
|
|
6989
7171
|
"tools.dimOutput",
|
|
6990
7172
|
"tools.collapseAfterTurn",
|
|
7173
|
+
"tools.collapseMutatingTools",
|
|
6991
7174
|
"compatibility.allowSafePatches",
|
|
6992
7175
|
"compatibility.allowCorePatches",
|
|
6993
7176
|
"compatibility.preferExistingEditor",
|
|
@@ -7458,6 +7641,7 @@ var allowedPaths = /* @__PURE__ */ new Set([
|
|
|
7458
7641
|
"tools.dimOutput",
|
|
7459
7642
|
"tools.showElapsed",
|
|
7460
7643
|
"tools.collapseAfterTurn",
|
|
7644
|
+
"tools.collapseMutatingTools",
|
|
7461
7645
|
"theme.nerdFonts",
|
|
7462
7646
|
"theme.terminalBackgroundSync",
|
|
7463
7647
|
"theme.autoApply",
|
|
@@ -7486,6 +7670,7 @@ function validatePathValue(path, value) {
|
|
|
7486
7670
|
"tools.showElapsed",
|
|
7487
7671
|
"tools.dimOutput",
|
|
7488
7672
|
"tools.collapseAfterTurn",
|
|
7673
|
+
"tools.collapseMutatingTools",
|
|
7489
7674
|
"compatibility.allowSafePatches",
|
|
7490
7675
|
"compatibility.allowCorePatches",
|
|
7491
7676
|
"compatibility.preferExistingEditor",
|
|
@@ -8205,6 +8390,7 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8205
8390
|
onSnapshot;
|
|
8206
8391
|
semantic;
|
|
8207
8392
|
disposed = false;
|
|
8393
|
+
renderPlanCache;
|
|
8208
8394
|
constructor(tui, theme, keybindings, options) {
|
|
8209
8395
|
super(tui, theme, keybindings);
|
|
8210
8396
|
this.config = options.config;
|
|
@@ -8234,61 +8420,23 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8234
8420
|
invalidate() {
|
|
8235
8421
|
super.invalidate();
|
|
8236
8422
|
this.semantic = semanticTheme(this.piTheme, this.config);
|
|
8423
|
+
this.renderPlanCache = void 0;
|
|
8237
8424
|
this.tui.requestRender();
|
|
8238
8425
|
}
|
|
8239
8426
|
render(width) {
|
|
8240
8427
|
if (width <= 0) return [];
|
|
8241
|
-
const
|
|
8242
|
-
const
|
|
8243
|
-
if (
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
const promptWidth2 = widthOf(prompt2) + 1;
|
|
8247
|
-
const prefix2 = `${" ".repeat(padding2)}${prompt2} `;
|
|
8248
|
-
const continuation2 = " ".repeat(padding2 + promptWidth2);
|
|
8249
|
-
const borderIndex = nativeLines.slice(1).findIndex((line) => isNativeBorderLine(line));
|
|
8250
|
-
const split = borderIndex >= 0 ? borderIndex + 1 : nativeLines.length;
|
|
8251
|
-
const body2 = nativeLines.slice(1, split);
|
|
8252
|
-
const dropdown = nativeLines.slice(split);
|
|
8253
|
-
const border = this.borderFor();
|
|
8254
|
-
const kind2 = this.frameKind(style);
|
|
8255
|
-
const renderWidth2 = width - (kind2 === "rounded" ? 2 : 0);
|
|
8256
|
-
const sideColor = kind2 === "rounded" ? this.borderColorFor() : void 0;
|
|
8257
|
-
const wrap = (line) => kind2 === "rounded" && sideColor ? `${sideColor("\u2502")}${line}${sideColor("\u2502")}` : line;
|
|
8258
|
-
const bashHidden2 = this.bashHiddenCount();
|
|
8259
|
-
const renderedBody2 = body2.map((line, index) => {
|
|
8260
|
-
const source = index === 0 && bashHidden2 > 0 ? stripLeadingVisibleChars(line, bashHidden2) : line;
|
|
8261
|
-
return wrap(widthSafe(`${index === 0 ? prefix2 : continuation2}${source}`, renderWidth2));
|
|
8262
|
-
});
|
|
8263
|
-
const dropdownLines = dropdown.map((line) => wrap(widthSafe(line, renderWidth2)));
|
|
8264
|
-
if (kind2 === "rounded") {
|
|
8265
|
-
return [
|
|
8266
|
-
border(`\u256D${"\u2500".repeat(Math.max(0, width - 2))}\u256E`),
|
|
8267
|
-
...renderedBody2,
|
|
8268
|
-
...dropdownLines,
|
|
8269
|
-
border(`\u2570${"\u2500".repeat(Math.max(0, width - 2))}\u256F`)
|
|
8270
|
-
];
|
|
8271
|
-
}
|
|
8272
|
-
return [border("\u2500".repeat(width)), ...renderedBody2, ...dropdownLines, border("\u2500".repeat(width))];
|
|
8273
|
-
}
|
|
8274
|
-
if (style === "native") return nativeLines.map((line) => widthSafe(line, width));
|
|
8275
|
-
const prompt = this.prompt();
|
|
8276
|
-
const promptWidth = widthOf(prompt) + 1;
|
|
8277
|
-
const padding = this.paddingFor(width, style);
|
|
8278
|
-
const kind = this.frameKind(style);
|
|
8279
|
-
const sideReserve = kind === "rounded" ? 2 : 0;
|
|
8280
|
-
const renderWidth = Math.max(1, width - sideReserve);
|
|
8281
|
-
const innerWidth = Math.max(1, renderWidth - promptWidth - padding * 2);
|
|
8282
|
-
const innerLines = super.render(innerWidth);
|
|
8428
|
+
const plan = this.renderPlan(width);
|
|
8429
|
+
const autocompleteState = this.autocompleteState;
|
|
8430
|
+
if (plan.style === "native") return super.render(width).map((line) => widthSafe(line, width));
|
|
8431
|
+
if (autocompleteState) return this.renderAutocompleteFrame(width, plan);
|
|
8432
|
+
const innerLines = super.render(plan.innerWidth);
|
|
8283
8433
|
if (innerLines.length === 0) return [];
|
|
8284
8434
|
const body = innerLines.slice(1, -1);
|
|
8285
|
-
const prefix = `${" ".repeat(padding)}${prompt} `;
|
|
8286
|
-
const continuation = " ".repeat(padding + promptWidth);
|
|
8287
8435
|
const hint = this.config.editor.hint;
|
|
8288
8436
|
const showHint = hint !== "" && this.getText() === "";
|
|
8289
8437
|
const bashHidden = this.bashHiddenCount();
|
|
8290
8438
|
const renderedBody = body.map((line, index) => {
|
|
8291
|
-
const lead = index === 0 ? prefix : continuation;
|
|
8439
|
+
const lead = index === 0 ? plan.prefix : plan.continuation;
|
|
8292
8440
|
const source = index === 0 && bashHidden > 0 ? stripLeadingVisibleChars(line, bashHidden) : line;
|
|
8293
8441
|
let content = `${lead}${source}`;
|
|
8294
8442
|
if (showHint && index === 0 && line) {
|
|
@@ -8297,10 +8445,10 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8297
8445
|
if (end < content.length) content = content.slice(0, end);
|
|
8298
8446
|
content += this.semantic.apply("hint", hint);
|
|
8299
8447
|
}
|
|
8300
|
-
return widthSafe(content, renderWidth);
|
|
8448
|
+
return widthSafe(content, plan.renderWidth);
|
|
8301
8449
|
});
|
|
8302
|
-
const metadata = this.metadata(width, style);
|
|
8303
|
-
const framed = this.frame(width, style, renderedBody, metadata);
|
|
8450
|
+
const metadata = this.metadata(width, plan.style);
|
|
8451
|
+
const framed = this.frame(width, plan.style, renderedBody, metadata);
|
|
8304
8452
|
return framed.map((line) => widthSafe(line, width));
|
|
8305
8453
|
}
|
|
8306
8454
|
dispose() {
|
|
@@ -8308,6 +8456,59 @@ var StyledEditor = class extends CustomEditor {
|
|
|
8308
8456
|
this.disposed = true;
|
|
8309
8457
|
this.invalidate();
|
|
8310
8458
|
}
|
|
8459
|
+
renderPlan(width) {
|
|
8460
|
+
const style = this.styleFor(width);
|
|
8461
|
+
const prompt = this.prompt();
|
|
8462
|
+
const kind = this.frameKind(style);
|
|
8463
|
+
const key = `${width}:${style}:${kind}:${prompt}`;
|
|
8464
|
+
if (this.renderPlanCache?.key === key) return this.renderPlanCache.plan;
|
|
8465
|
+
const promptWidth = widthOf(prompt) + 1;
|
|
8466
|
+
const padding = this.paddingFor(width, style);
|
|
8467
|
+
const sideReserve = kind === "rounded" ? 2 : 0;
|
|
8468
|
+
const renderWidth = Math.max(1, width - sideReserve);
|
|
8469
|
+
const innerWidth = Math.max(1, renderWidth - promptWidth - padding * 2);
|
|
8470
|
+
const prefix = `${" ".repeat(padding)}${prompt} `;
|
|
8471
|
+
const continuation = " ".repeat(padding + promptWidth);
|
|
8472
|
+
const plan = {
|
|
8473
|
+
style,
|
|
8474
|
+
kind,
|
|
8475
|
+
prompt,
|
|
8476
|
+
promptWidth,
|
|
8477
|
+
padding,
|
|
8478
|
+
sideReserve,
|
|
8479
|
+
renderWidth,
|
|
8480
|
+
innerWidth,
|
|
8481
|
+
prefix,
|
|
8482
|
+
continuation
|
|
8483
|
+
};
|
|
8484
|
+
this.renderPlanCache = { key, plan };
|
|
8485
|
+
return plan;
|
|
8486
|
+
}
|
|
8487
|
+
renderAutocompleteFrame(width, plan) {
|
|
8488
|
+
const nativeLines = super.render(width);
|
|
8489
|
+
const borderIndex = nativeLines.slice(1).findIndex((line) => isNativeBorderLine(line));
|
|
8490
|
+
const split = borderIndex >= 0 ? borderIndex + 1 : nativeLines.length;
|
|
8491
|
+
const body = nativeLines.slice(1, split);
|
|
8492
|
+
const dropdown = nativeLines.slice(split);
|
|
8493
|
+
const border = this.borderFor();
|
|
8494
|
+
const sideColor = plan.kind === "rounded" ? this.borderColorFor() : void 0;
|
|
8495
|
+
const wrap = (line) => plan.kind === "rounded" && sideColor ? `${sideColor("\u2502")}${line}${sideColor("\u2502")}` : line;
|
|
8496
|
+
const bashHidden = this.bashHiddenCount();
|
|
8497
|
+
const renderedBody = body.map((line, index) => {
|
|
8498
|
+
const source = index === 0 && bashHidden > 0 ? stripLeadingVisibleChars(line, bashHidden) : line;
|
|
8499
|
+
return wrap(widthSafe(`${index === 0 ? plan.prefix : plan.continuation}${source}`, plan.renderWidth));
|
|
8500
|
+
});
|
|
8501
|
+
const dropdownLines = dropdown.map((line) => wrap(widthSafe(line, plan.renderWidth)));
|
|
8502
|
+
if (plan.kind === "rounded") {
|
|
8503
|
+
return [
|
|
8504
|
+
border(`\u256D${"\u2500".repeat(Math.max(0, width - 2))}\u256E`),
|
|
8505
|
+
...renderedBody,
|
|
8506
|
+
...dropdownLines,
|
|
8507
|
+
border(`\u2570${"\u2500".repeat(Math.max(0, width - 2))}\u256F`)
|
|
8508
|
+
];
|
|
8509
|
+
}
|
|
8510
|
+
return [border("\u2500".repeat(width)), ...renderedBody, ...dropdownLines, border("\u2500".repeat(width))];
|
|
8511
|
+
}
|
|
8311
8512
|
prompt() {
|
|
8312
8513
|
if (this.isBashMode()) {
|
|
8313
8514
|
const glyph = this.semantic.glyph("bashPrompt");
|
|
@@ -8549,13 +8750,13 @@ function resolveAccentRgb(resolved) {
|
|
|
8549
8750
|
function styledLogoLines(resolved) {
|
|
8550
8751
|
const accent = resolveAccentRgb(resolved);
|
|
8551
8752
|
if (!accent) return [...PI_LOGO_LINES];
|
|
8552
|
-
const
|
|
8553
|
-
if (
|
|
8753
|
+
const cacheKey2 = `${resolved.color("accent")}|${resolved.mode}`;
|
|
8754
|
+
if (cacheKey2 === logoGradientCacheKey && logoGradientCacheLines) return logoGradientCacheLines;
|
|
8554
8755
|
const palette = buildLogoPalette(accent);
|
|
8555
8756
|
logoGradientCacheLines = PI_LOGO_LINES.map(
|
|
8556
8757
|
(line, rowIndex) => renderLogoGradientLine(line, palette, rowIndex * LOGO_ROW_PHASE_STEP)
|
|
8557
8758
|
);
|
|
8558
|
-
logoGradientCacheKey =
|
|
8759
|
+
logoGradientCacheKey = cacheKey2;
|
|
8559
8760
|
return logoGradientCacheLines;
|
|
8560
8761
|
}
|
|
8561
8762
|
function compactLogoHeader(resolved, details, width) {
|
|
@@ -9618,16 +9819,51 @@ var RenderScheduler = class {
|
|
|
9618
9819
|
|
|
9619
9820
|
// extension-src/pi-style/app/snapshot.ts
|
|
9620
9821
|
function createSnapshot(generation2, revision = 0, values = {}) {
|
|
9621
|
-
return Object.freeze({ generation: generation2, revision
|
|
9822
|
+
return Object.freeze({ ...values, generation: generation2, revision });
|
|
9622
9823
|
}
|
|
9623
9824
|
function replaceSnapshot(current, generation2, values) {
|
|
9624
9825
|
if (current.generation !== generation2) return current;
|
|
9625
|
-
return createSnapshot(generation2, current.revision + 1, values);
|
|
9826
|
+
return equalStatusSnapshot(current, values) ? current : createSnapshot(generation2, current.revision + 1, values);
|
|
9827
|
+
}
|
|
9828
|
+
function equalStatusSnapshot(current, next) {
|
|
9829
|
+
const currentEntries = Object.entries(current).filter(([key]) => key !== "generation" && key !== "revision");
|
|
9830
|
+
const nextEntries = Object.entries(next).filter(([key]) => key !== "generation" && key !== "revision");
|
|
9831
|
+
if (currentEntries.length !== nextEntries.length) return false;
|
|
9832
|
+
for (const [key, value] of nextEntries) {
|
|
9833
|
+
if (!hasOwn(current, key)) return false;
|
|
9834
|
+
if (!equalValue(current[key], value)) return false;
|
|
9835
|
+
}
|
|
9836
|
+
return true;
|
|
9837
|
+
}
|
|
9838
|
+
function equalValue(left, right) {
|
|
9839
|
+
if (Object.is(left, right)) return true;
|
|
9840
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
9841
|
+
if (left.length !== right.length) return false;
|
|
9842
|
+
for (let index = 0; index < left.length; index++) {
|
|
9843
|
+
if (!equalValue(left[index], right[index])) return false;
|
|
9844
|
+
}
|
|
9845
|
+
return true;
|
|
9846
|
+
}
|
|
9847
|
+
if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
|
|
9848
|
+
const leftKeys = Object.keys(left);
|
|
9849
|
+
const rightKeys = Object.keys(right);
|
|
9850
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
9851
|
+
for (const key of rightKeys) {
|
|
9852
|
+
if (!hasOwn(left, key)) return false;
|
|
9853
|
+
if (!equalValue(left[key], right[key])) return false;
|
|
9854
|
+
}
|
|
9855
|
+
return true;
|
|
9856
|
+
}
|
|
9857
|
+
function hasOwn(value, key) {
|
|
9858
|
+
return Object.hasOwn(value, key);
|
|
9859
|
+
}
|
|
9860
|
+
function isPlainRecord(value) {
|
|
9861
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9626
9862
|
}
|
|
9627
9863
|
|
|
9628
9864
|
// extension-src/pi-style/app/runtime.ts
|
|
9629
|
-
function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
9630
|
-
}) {
|
|
9865
|
+
function createPiStyleRuntime(host, generation2, requestRender = host.requestRender ?? (() => {
|
|
9866
|
+
})) {
|
|
9631
9867
|
let disposed = false;
|
|
9632
9868
|
const extensionStatuses = () => {
|
|
9633
9869
|
if (!host.extensionStatusProvider) return void 0;
|
|
@@ -9638,13 +9874,22 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9638
9874
|
return [];
|
|
9639
9875
|
}
|
|
9640
9876
|
};
|
|
9877
|
+
const contextSnapshot = () => {
|
|
9878
|
+
const usage = host.getContextUsage?.();
|
|
9879
|
+
if (!usage) return void 0;
|
|
9880
|
+
return {
|
|
9881
|
+
...usage.tokens !== null ? { currentTokens: usage.tokens } : {},
|
|
9882
|
+
windowTokens: usage.contextWindow,
|
|
9883
|
+
...usage.percent !== null ? { percent: usage.percent } : {}
|
|
9884
|
+
};
|
|
9885
|
+
};
|
|
9641
9886
|
let currentConfig = host.config;
|
|
9642
9887
|
const disposables = new DisposableStore();
|
|
9643
9888
|
const scheduler = new RenderScheduler({ requestRender }, generation2, () => !disposed);
|
|
9644
9889
|
const git = new CachedGitProvider(host.gitRunner);
|
|
9645
9890
|
const contextProvider = new InMemoryContextProvider();
|
|
9646
9891
|
const usageProvider = new InMemoryUsageProvider();
|
|
9647
|
-
const
|
|
9892
|
+
const initialContext = contextSnapshot();
|
|
9648
9893
|
const initialStatuses = extensionStatuses();
|
|
9649
9894
|
const initialValues = {
|
|
9650
9895
|
...initialStatuses ? { extensionStatuses: initialStatuses } : {},
|
|
@@ -9653,13 +9898,7 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9653
9898
|
...host.model?.reasoning !== void 0 ? { reasoning: host.model.reasoning } : {},
|
|
9654
9899
|
...host.thinkingLevel ? { thinkingLevel: normalizeThinkingLevel(host.thinkingLevel) } : {},
|
|
9655
9900
|
...host.cwd ? { cwd: host.cwd } : {},
|
|
9656
|
-
...
|
|
9657
|
-
context: {
|
|
9658
|
-
...usage.tokens !== null ? { currentTokens: usage.tokens } : {},
|
|
9659
|
-
windowTokens: usage.contextWindow,
|
|
9660
|
-
...usage.percent !== null ? { percent: usage.percent } : {}
|
|
9661
|
-
}
|
|
9662
|
-
} : {}
|
|
9901
|
+
...initialContext ? { context: initialContext } : {}
|
|
9663
9902
|
};
|
|
9664
9903
|
let currentSnapshot = createSnapshot(generation2, 0, initialValues);
|
|
9665
9904
|
let statusLine;
|
|
@@ -9670,14 +9909,35 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9670
9909
|
editor: "disabled",
|
|
9671
9910
|
startup: "disabled"
|
|
9672
9911
|
};
|
|
9673
|
-
const
|
|
9912
|
+
const snapshotValues = (snapshot) => {
|
|
9913
|
+
const { generation: _generation, revision: _revision, ...values } = snapshot;
|
|
9914
|
+
return values;
|
|
9915
|
+
};
|
|
9916
|
+
const withSnapshotPatch = (patch) => {
|
|
9917
|
+
const next = { ...snapshotValues(currentSnapshot) };
|
|
9918
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
9919
|
+
if (value === void 0) delete next[key];
|
|
9920
|
+
else next[key] = value;
|
|
9921
|
+
}
|
|
9922
|
+
return next;
|
|
9923
|
+
};
|
|
9924
|
+
const createStartupSnapshot = (config, resources = host.resources) => ({
|
|
9674
9925
|
...currentSnapshot,
|
|
9675
9926
|
reason: host.startupReason ?? "startup",
|
|
9676
9927
|
...host.provider ? { startupProvider: host.provider } : {},
|
|
9677
9928
|
...host.cwd ? { project: host.cwd.split(/[\\/]/).filter(Boolean).at(-1) } : {},
|
|
9678
9929
|
preset: config.preset,
|
|
9679
|
-
...
|
|
9930
|
+
...resources ? { resources } : {}
|
|
9680
9931
|
});
|
|
9932
|
+
const applySnapshot = (nextSnapshot) => {
|
|
9933
|
+
if (nextSnapshot === currentSnapshot) return false;
|
|
9934
|
+
currentSnapshot = nextSnapshot;
|
|
9935
|
+
statusLine?.update(currentSnapshot);
|
|
9936
|
+
editor?.update(currentSnapshot);
|
|
9937
|
+
startup?.update(createStartupSnapshot(currentConfig));
|
|
9938
|
+
return true;
|
|
9939
|
+
};
|
|
9940
|
+
const updateSnapshot = (values) => applySnapshot(replaceSnapshot(currentSnapshot, generation2, values));
|
|
9681
9941
|
const installStatus = () => {
|
|
9682
9942
|
if (!host.hasUI || host.mode !== "tui" || !host.ui || !currentConfig.enabled || !currentConfig.statusLine.enabled) {
|
|
9683
9943
|
installationState = { ...installationState, status: "disabled" };
|
|
@@ -9740,7 +10000,7 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9740
10000
|
startup = installStartup({
|
|
9741
10001
|
host: { ...host.ui, mode: host.mode, hasUI: host.hasUI },
|
|
9742
10002
|
config: currentConfig,
|
|
9743
|
-
snapshot:
|
|
10003
|
+
snapshot: createStartupSnapshot(currentConfig),
|
|
9744
10004
|
generation: generation2,
|
|
9745
10005
|
requestRender,
|
|
9746
10006
|
timeoutMs: 3e3,
|
|
@@ -9789,27 +10049,10 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9789
10049
|
if (host.cwd && currentConfig.enabled && currentConfig.statusLine.enabled) {
|
|
9790
10050
|
void git.get(host.cwd).then((value) => {
|
|
9791
10051
|
if (disposed) return;
|
|
9792
|
-
|
|
9793
|
-
statusLine?.update(currentSnapshot);
|
|
9794
|
-
editor?.update(currentSnapshot);
|
|
9795
|
-
startup?.update({
|
|
9796
|
-
...currentSnapshot,
|
|
9797
|
-
reason: host.startupReason ?? "startup",
|
|
9798
|
-
...host.provider ? { startupProvider: host.provider } : {},
|
|
9799
|
-
...host.cwd ? { project: host.cwd.split(/[\\/]/).filter(Boolean).at(-1) } : {},
|
|
9800
|
-
preset: currentConfig.preset,
|
|
9801
|
-
...host.resources ? { resources: host.resources } : {}
|
|
9802
|
-
});
|
|
9803
|
-
requestRender();
|
|
9804
|
-
});
|
|
9805
|
-
}
|
|
9806
|
-
if (usage) {
|
|
9807
|
-
contextProvider.set("active", {
|
|
9808
|
-
...usage.tokens !== null ? { currentTokens: usage.tokens } : {},
|
|
9809
|
-
windowTokens: usage.contextWindow,
|
|
9810
|
-
...usage.percent !== null ? { percent: usage.percent } : {}
|
|
10052
|
+
if (updateSnapshot(withSnapshotPatch({ git: value }))) requestRender();
|
|
9811
10053
|
});
|
|
9812
10054
|
}
|
|
10055
|
+
if (initialContext) contextProvider.set("active", initialContext);
|
|
9813
10056
|
return {
|
|
9814
10057
|
generation: generation2,
|
|
9815
10058
|
providerIdentity: { git, context: contextProvider, usage: usageProvider },
|
|
@@ -9818,49 +10061,33 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9818
10061
|
},
|
|
9819
10062
|
mode: host.mode,
|
|
9820
10063
|
hasUI: host.hasUI,
|
|
9821
|
-
snapshot
|
|
10064
|
+
get snapshot() {
|
|
10065
|
+
return currentSnapshot;
|
|
10066
|
+
},
|
|
9822
10067
|
disposables,
|
|
9823
10068
|
scheduler,
|
|
9824
10069
|
updateStartupResources(resources) {
|
|
9825
|
-
if (disposed) return;
|
|
9826
|
-
startup
|
|
9827
|
-
...currentSnapshot,
|
|
9828
|
-
reason: host.startupReason ?? "startup",
|
|
9829
|
-
...host.provider ? { startupProvider: host.provider } : {},
|
|
9830
|
-
...host.cwd ? { project: host.cwd.split(/[\\/]/).filter(Boolean).at(-1) } : {},
|
|
9831
|
-
preset: currentConfig.preset,
|
|
9832
|
-
resources
|
|
9833
|
-
});
|
|
10070
|
+
if (disposed || !startup) return false;
|
|
10071
|
+
startup.update(createStartupSnapshot(currentConfig, resources));
|
|
9834
10072
|
requestRender();
|
|
10073
|
+
return true;
|
|
9835
10074
|
},
|
|
9836
10075
|
dismissStartup() {
|
|
9837
10076
|
startup?.dismiss();
|
|
9838
10077
|
},
|
|
9839
|
-
update(values) {
|
|
9840
|
-
if (disposed) return;
|
|
9841
|
-
const
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
...statuses ? { extensionStatuses: statuses } : {}
|
|
9853
|
-
});
|
|
9854
|
-
statusLine?.update(currentSnapshot);
|
|
9855
|
-
editor?.update(currentSnapshot);
|
|
9856
|
-
startup?.update({
|
|
9857
|
-
...currentSnapshot,
|
|
9858
|
-
reason: host.startupReason ?? "startup",
|
|
9859
|
-
...host.provider ? { startupProvider: host.provider } : {},
|
|
9860
|
-
...host.cwd ? { project: host.cwd.split(/[\\/]/).filter(Boolean).at(-1) } : {},
|
|
9861
|
-
preset: currentConfig.preset,
|
|
9862
|
-
...host.resources ? { resources: host.resources } : {}
|
|
9863
|
-
});
|
|
10078
|
+
update(values, options = {}) {
|
|
10079
|
+
if (disposed) return false;
|
|
10080
|
+
const patch = {
|
|
10081
|
+
...values
|
|
10082
|
+
};
|
|
10083
|
+
if (options.refreshContextUsage) {
|
|
10084
|
+
const context = contextSnapshot();
|
|
10085
|
+
patch.context = context;
|
|
10086
|
+
if (context) contextProvider.set("active", context);
|
|
10087
|
+
else contextProvider.clear();
|
|
10088
|
+
}
|
|
10089
|
+
if (options.refreshExtensionStatuses) patch.extensionStatuses = extensionStatuses();
|
|
10090
|
+
return updateSnapshot(withSnapshotPatch(patch));
|
|
9864
10091
|
},
|
|
9865
10092
|
configure(nextConfig) {
|
|
9866
10093
|
if (disposed) return;
|
|
@@ -9895,17 +10122,7 @@ function createPiStyleRuntime(host, generation2, requestRender = () => {
|
|
|
9895
10122
|
git.invalidate(host.cwd);
|
|
9896
10123
|
void git.get(host.cwd).then((value) => {
|
|
9897
10124
|
if (disposed) return;
|
|
9898
|
-
|
|
9899
|
-
statusLine?.update(currentSnapshot);
|
|
9900
|
-
editor?.update(currentSnapshot);
|
|
9901
|
-
startup?.update({
|
|
9902
|
-
...currentSnapshot,
|
|
9903
|
-
reason: host.startupReason ?? "startup",
|
|
9904
|
-
...host.provider ? { startupProvider: host.provider } : {},
|
|
9905
|
-
...host.cwd ? { project: host.cwd.split(/[\\/]/).filter(Boolean).at(-1) } : {},
|
|
9906
|
-
preset: currentConfig.preset
|
|
9907
|
-
});
|
|
9908
|
-
requestRender();
|
|
10125
|
+
if (updateSnapshot(withSnapshotPatch({ git: value }))) requestRender();
|
|
9909
10126
|
});
|
|
9910
10127
|
},
|
|
9911
10128
|
get disposed() {
|
|
@@ -10109,10 +10326,10 @@ function createPiStyleApp(initialConfig, reloadPort, onConfigChange) {
|
|
|
10109
10326
|
sessionShutdown() {
|
|
10110
10327
|
runtime.stop();
|
|
10111
10328
|
},
|
|
10112
|
-
update(values, kind = "coalesced") {
|
|
10329
|
+
update(values, kind = "coalesced", options) {
|
|
10113
10330
|
const active = runtime.current;
|
|
10114
10331
|
if (!active) return;
|
|
10115
|
-
active.update(values);
|
|
10332
|
+
if (!active.update(values, options)) return;
|
|
10116
10333
|
active.scheduler.schedule(kind);
|
|
10117
10334
|
},
|
|
10118
10335
|
reload() {
|
|
@@ -10415,9 +10632,11 @@ function renderBashExecutionBox(instance, args) {
|
|
|
10415
10632
|
try {
|
|
10416
10633
|
if (host.piStyleStart === void 0) host.piStyleStart = Date.now();
|
|
10417
10634
|
const renderedWidth = boxWidth(width);
|
|
10635
|
+
const cacheKey2 = `${themeCacheKey(theme)}|${width}|${host.status}|${host.exitCode ?? ""}|${host.command}`;
|
|
10636
|
+
if (host.status !== "running" && host.piStyleRenderCache?.key === cacheKey2) return host.piStyleRenderCache.lines;
|
|
10418
10637
|
const inner = boxInnerWidth(renderedWidth);
|
|
10419
10638
|
const wrapped = content.render(inner).map((line) => boxLine(theme, line.startsWith(" ") ? line.slice(1) : line, renderedWidth));
|
|
10420
|
-
|
|
10639
|
+
const lines = [
|
|
10421
10640
|
"",
|
|
10422
10641
|
boxLabeledBorder(theme, TOP_LEFT, TOP_RIGHT, bashBoxTitle(theme, host), void 0, renderedWidth),
|
|
10423
10642
|
boxBlankLine(theme, renderedWidth),
|
|
@@ -10425,6 +10644,8 @@ function renderBashExecutionBox(instance, args) {
|
|
|
10425
10644
|
boxBlankLine(theme, renderedWidth),
|
|
10426
10645
|
boxLabeledBorder(theme, BOTTOM_LEFT, BOTTOM_RIGHT, bashBoxFooter(theme, host), void 0, renderedWidth)
|
|
10427
10646
|
];
|
|
10647
|
+
if (host.status !== "running") host.piStyleRenderCache = { key: cacheKey2, lines };
|
|
10648
|
+
return lines;
|
|
10428
10649
|
} catch {
|
|
10429
10650
|
return void 0;
|
|
10430
10651
|
}
|
|
@@ -10455,17 +10676,27 @@ import {
|
|
|
10455
10676
|
} from "@earendil-works/pi-coding-agent";
|
|
10456
10677
|
|
|
10457
10678
|
// extension-src/pi-style/features/messages/index.ts
|
|
10458
|
-
import { visibleWidth as visibleWidth4 } from "@earendil-works/pi-tui";
|
|
10459
10679
|
var OSC133_ZONE_START = "\x1B]133;A\x07";
|
|
10460
10680
|
var OSC133_ZONE_END = "\x1B]133;B\x07";
|
|
10461
10681
|
var OSC133_ZONE_FINAL = "\x1B]133;C\x07";
|
|
10682
|
+
var BG_RESET = "\x1B[49m";
|
|
10683
|
+
var MAX_RENDER_CACHE_KEYS_PER_INSTANCE = 8;
|
|
10684
|
+
var MAX_LINE_ANALYSIS_ENTRIES = 4096;
|
|
10685
|
+
var renderCacheByInstance = /* @__PURE__ */ new WeakMap();
|
|
10686
|
+
var lineAnalysisCache = /* @__PURE__ */ new Map();
|
|
10687
|
+
var messageDecorationTestState = {
|
|
10688
|
+
decoratePasses: 0,
|
|
10689
|
+
cacheHits: 0,
|
|
10690
|
+
cacheMisses: 0,
|
|
10691
|
+
lineCacheHits: 0,
|
|
10692
|
+
lineCacheMisses: 0
|
|
10693
|
+
};
|
|
10462
10694
|
function extractOscEnvelope(line) {
|
|
10463
10695
|
if (!line.startsWith(OSC133_ZONE_START)) return void 0;
|
|
10464
10696
|
const bodyEnd = line.indexOf(OSC133_ZONE_END, OSC133_ZONE_START.length);
|
|
10465
10697
|
if (bodyEnd < 0 || !line.endsWith(OSC133_ZONE_FINAL)) return void 0;
|
|
10466
10698
|
return { start: OSC133_ZONE_START, body: line.slice(OSC133_ZONE_START.length, bodyEnd), end: line.slice(bodyEnd) };
|
|
10467
10699
|
}
|
|
10468
|
-
var BG_RESET = "\x1B[49m";
|
|
10469
10700
|
function splitLeadingMarkers(line) {
|
|
10470
10701
|
let index = 0;
|
|
10471
10702
|
while (line.startsWith("\x1B]", index)) {
|
|
@@ -10497,39 +10728,6 @@ function isBackgroundSgr(sequence) {
|
|
|
10497
10728
|
}
|
|
10498
10729
|
return false;
|
|
10499
10730
|
}
|
|
10500
|
-
function rebuildAtWidth(line, width, lead) {
|
|
10501
|
-
const { head, rest } = splitLeadingMarkers(line);
|
|
10502
|
-
const bgAnsi = leadingSgr(rest);
|
|
10503
|
-
if (bgAnsi && isBackgroundSgr(bgAnsi) && rest.endsWith(BG_RESET)) {
|
|
10504
|
-
const body = rest.slice(bgAnsi.length, rest.length - BG_RESET.length);
|
|
10505
|
-
const pad = " ".repeat(Math.max(0, width - visibleWidth4(lead) - visibleWidth4(body)));
|
|
10506
|
-
return `${head}${bgAnsi}${lead}${body}${pad}${BG_RESET}`;
|
|
10507
|
-
}
|
|
10508
|
-
const padded = `${lead}${line}`;
|
|
10509
|
-
return `${padded}${" ".repeat(Math.max(0, width - visibleWidth4(padded)))}`;
|
|
10510
|
-
}
|
|
10511
|
-
function decorateMessageLine(line, index, lastIndex, contentIndex, width, options) {
|
|
10512
|
-
const { firstEnvelope, firstHasStart, multilineEnvelope, prefix } = options;
|
|
10513
|
-
const prefixWidth = visibleWidth4(prefix);
|
|
10514
|
-
const lead = index === contentIndex ? prefix : index > contentIndex ? " ".repeat(prefixWidth) : "";
|
|
10515
|
-
if (index === contentIndex && firstEnvelope)
|
|
10516
|
-
return `${firstEnvelope.start}${rebuildAtWidth(firstEnvelope.body, width, prefix)}${firstEnvelope.end}`;
|
|
10517
|
-
if (index === contentIndex && firstHasStart)
|
|
10518
|
-
return `${OSC133_ZONE_START}${rebuildAtWidth(line.slice(OSC133_ZONE_START.length), width, prefix)}`;
|
|
10519
|
-
if (index === lastIndex && multilineEnvelope && index !== contentIndex)
|
|
10520
|
-
return `${OSC133_ZONE_END}${OSC133_ZONE_FINAL}${rebuildAtWidth(
|
|
10521
|
-
line.slice((OSC133_ZONE_END + OSC133_ZONE_FINAL).length),
|
|
10522
|
-
width,
|
|
10523
|
-
lead
|
|
10524
|
-
)}`;
|
|
10525
|
-
if (index === contentIndex && index === lastIndex && multilineEnvelope && line.startsWith(OSC133_ZONE_END + OSC133_ZONE_FINAL))
|
|
10526
|
-
return `${OSC133_ZONE_END}${OSC133_ZONE_FINAL}${rebuildAtWidth(
|
|
10527
|
-
line.slice((OSC133_ZONE_END + OSC133_ZONE_FINAL).length),
|
|
10528
|
-
width,
|
|
10529
|
-
prefix
|
|
10530
|
-
)}`;
|
|
10531
|
-
return rebuildAtWidth(line, width, lead);
|
|
10532
|
-
}
|
|
10533
10731
|
function contentText(line) {
|
|
10534
10732
|
let output = "";
|
|
10535
10733
|
for (let index = 0; index < line.length; index++) {
|
|
@@ -10553,32 +10751,127 @@ function contentText(line) {
|
|
|
10553
10751
|
function hasContent(line) {
|
|
10554
10752
|
return [...contentText(line)].some((character) => !/\s/u.test(character));
|
|
10555
10753
|
}
|
|
10754
|
+
function getLineAnalysis(line) {
|
|
10755
|
+
const cached = lineAnalysisCache.get(line);
|
|
10756
|
+
if (cached) {
|
|
10757
|
+
messageDecorationTestState.lineCacheHits++;
|
|
10758
|
+
return cached;
|
|
10759
|
+
}
|
|
10760
|
+
messageDecorationTestState.lineCacheMisses++;
|
|
10761
|
+
const leadingMarkers = splitLeadingMarkers(line);
|
|
10762
|
+
const backgroundAnsi = leadingSgr(leadingMarkers.rest);
|
|
10763
|
+
const isBackgroundWrapped = backgroundAnsi !== "" && isBackgroundSgr(backgroundAnsi) && leadingMarkers.rest.endsWith(BG_RESET);
|
|
10764
|
+
const backgroundBody = isBackgroundWrapped ? leadingMarkers.rest.slice(backgroundAnsi.length, leadingMarkers.rest.length - BG_RESET.length) : void 0;
|
|
10765
|
+
const analysis = {
|
|
10766
|
+
visibleWidth: visibleWidth(line),
|
|
10767
|
+
hasContent: hasContent(line),
|
|
10768
|
+
oscEnvelope: extractOscEnvelope(line),
|
|
10769
|
+
hasOscStart: line.startsWith(OSC133_ZONE_START),
|
|
10770
|
+
leadingMarkers,
|
|
10771
|
+
isBackgroundWrapped,
|
|
10772
|
+
backgroundAnsi,
|
|
10773
|
+
backgroundBody,
|
|
10774
|
+
backgroundBodyWidth: backgroundBody === void 0 ? void 0 : visibleWidth(backgroundBody)
|
|
10775
|
+
};
|
|
10776
|
+
lineAnalysisCache.set(line, analysis);
|
|
10777
|
+
if (lineAnalysisCache.size > MAX_LINE_ANALYSIS_ENTRIES) {
|
|
10778
|
+
const oldestKey = lineAnalysisCache.keys().next().value;
|
|
10779
|
+
if (oldestKey !== void 0) lineAnalysisCache.delete(oldestKey);
|
|
10780
|
+
}
|
|
10781
|
+
return analysis;
|
|
10782
|
+
}
|
|
10783
|
+
function rebuildAtWidth(line, width, lead, leadWidth, analysis = getLineAnalysis(line)) {
|
|
10784
|
+
if (analysis.isBackgroundWrapped && analysis.backgroundBody !== void 0 && analysis.backgroundBodyWidth !== void 0) {
|
|
10785
|
+
const pad2 = " ".repeat(Math.max(0, width - leadWidth - analysis.backgroundBodyWidth));
|
|
10786
|
+
return `${analysis.leadingMarkers.head}${analysis.backgroundAnsi}${lead}${analysis.backgroundBody}${pad2}${BG_RESET}`;
|
|
10787
|
+
}
|
|
10788
|
+
const pad = " ".repeat(Math.max(0, width - leadWidth - analysis.visibleWidth));
|
|
10789
|
+
return `${lead}${line}${pad}`;
|
|
10790
|
+
}
|
|
10791
|
+
function decorateMessageLine(line, index, lastIndex, contentIndex, width, options, analysis = getLineAnalysis(line)) {
|
|
10792
|
+
const { firstEnvelope, firstHasStart, multilineEnvelope, prefix, prefixWidth } = options;
|
|
10793
|
+
const lead = index === contentIndex ? prefix : index > contentIndex ? " ".repeat(prefixWidth) : "";
|
|
10794
|
+
const leadWidth = index < contentIndex ? 0 : prefixWidth;
|
|
10795
|
+
if (index === contentIndex && firstEnvelope)
|
|
10796
|
+
return `${firstEnvelope.start}${rebuildAtWidth(firstEnvelope.body, width, prefix, prefixWidth)}${firstEnvelope.end}`;
|
|
10797
|
+
if (index === contentIndex && firstHasStart)
|
|
10798
|
+
return `${OSC133_ZONE_START}${rebuildAtWidth(line.slice(OSC133_ZONE_START.length), width, prefix, prefixWidth)}`;
|
|
10799
|
+
if (index === lastIndex && multilineEnvelope && index !== contentIndex)
|
|
10800
|
+
return `${OSC133_ZONE_END}${OSC133_ZONE_FINAL}${rebuildAtWidth(
|
|
10801
|
+
line.slice((OSC133_ZONE_END + OSC133_ZONE_FINAL).length),
|
|
10802
|
+
width,
|
|
10803
|
+
lead,
|
|
10804
|
+
leadWidth
|
|
10805
|
+
)}`;
|
|
10806
|
+
if (index === contentIndex && index === lastIndex && multilineEnvelope && line.startsWith(OSC133_ZONE_END + OSC133_ZONE_FINAL))
|
|
10807
|
+
return `${OSC133_ZONE_END}${OSC133_ZONE_FINAL}${rebuildAtWidth(
|
|
10808
|
+
line.slice((OSC133_ZONE_END + OSC133_ZONE_FINAL).length),
|
|
10809
|
+
width,
|
|
10810
|
+
prefix,
|
|
10811
|
+
prefixWidth
|
|
10812
|
+
)}`;
|
|
10813
|
+
return rebuildAtWidth(line, width, lead, leadWidth, analysis);
|
|
10814
|
+
}
|
|
10815
|
+
function sameLines(left, right) {
|
|
10816
|
+
if (left === right) return true;
|
|
10817
|
+
if (left.length !== right.length) return false;
|
|
10818
|
+
for (let index = 0; index < left.length; index++) {
|
|
10819
|
+
if (left[index] !== right[index]) return false;
|
|
10820
|
+
}
|
|
10821
|
+
return true;
|
|
10822
|
+
}
|
|
10823
|
+
function cacheKey(width, prefix) {
|
|
10824
|
+
return `${width}\0${prefix}`;
|
|
10825
|
+
}
|
|
10826
|
+
function getRenderCache(instance) {
|
|
10827
|
+
let cache = renderCacheByInstance.get(instance);
|
|
10828
|
+
if (!cache) {
|
|
10829
|
+
cache = /* @__PURE__ */ new Map();
|
|
10830
|
+
renderCacheByInstance.set(instance, cache);
|
|
10831
|
+
}
|
|
10832
|
+
return cache;
|
|
10833
|
+
}
|
|
10834
|
+
function storeRenderCache(instance, width, prefix, native, result) {
|
|
10835
|
+
const cache = getRenderCache(instance);
|
|
10836
|
+
const key = cacheKey(width, prefix);
|
|
10837
|
+
if (cache.has(key)) cache.delete(key);
|
|
10838
|
+
cache.set(key, { nativeRef: native, nativeLines: [...native], result: [...result] });
|
|
10839
|
+
while (cache.size > MAX_RENDER_CACHE_KEYS_PER_INSTANCE) {
|
|
10840
|
+
const oldestKey = cache.keys().next().value;
|
|
10841
|
+
if (oldestKey === void 0) break;
|
|
10842
|
+
cache.delete(oldestKey);
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10556
10845
|
function prefixNative(lines, width, prefix) {
|
|
10557
10846
|
if (!Array.isArray(lines) || lines.length === 0 || !lines.every((line) => typeof line === "string")) return void 0;
|
|
10847
|
+
messageDecorationTestState.decoratePasses++;
|
|
10558
10848
|
const nativeLines = lines;
|
|
10559
|
-
const prefixWidth =
|
|
10849
|
+
const prefixWidth = visibleWidth(prefix);
|
|
10560
10850
|
if (width <= prefixWidth) return void 0;
|
|
10561
10851
|
const bodyWidth = width - prefixWidth;
|
|
10562
10852
|
const first = nativeLines[0] ?? "";
|
|
10563
10853
|
const last = nativeLines.at(-1) ?? "";
|
|
10854
|
+
const lastAnalysis = getLineAnalysis(last);
|
|
10564
10855
|
const multilineEnvelope = nativeLines.length > 1 && last.startsWith(OSC133_ZONE_END + OSC133_ZONE_FINAL);
|
|
10565
10856
|
const firstContentIndex = nativeLines.findIndex((line, index) => {
|
|
10566
|
-
if (index !== nativeLines.length - 1 || !multilineEnvelope) return
|
|
10567
|
-
return !nativeLines.slice(0, index).some((earlier) =>
|
|
10857
|
+
if (index !== nativeLines.length - 1 || !multilineEnvelope) return getLineAnalysis(line).hasContent;
|
|
10858
|
+
return !nativeLines.slice(0, index).some((earlier) => getLineAnalysis(earlier).hasContent) && lastAnalysis.hasContent;
|
|
10568
10859
|
});
|
|
10569
10860
|
if (firstContentIndex < 0) return nativeLines;
|
|
10570
|
-
const
|
|
10571
|
-
const
|
|
10861
|
+
const firstAnalysis = getLineAnalysis(first);
|
|
10862
|
+
const firstEnvelope = firstContentIndex === 0 ? firstAnalysis.oscEnvelope : void 0;
|
|
10863
|
+
const firstHasStart = firstContentIndex === 0 && firstAnalysis.hasOscStart;
|
|
10572
10864
|
const decorated = nativeLines.map(
|
|
10573
10865
|
(line, index) => decorateMessageLine(line, index, nativeLines.length - 1, firstContentIndex, width, {
|
|
10574
10866
|
firstEnvelope,
|
|
10575
10867
|
firstHasStart,
|
|
10576
10868
|
multilineEnvelope,
|
|
10577
|
-
prefix
|
|
10869
|
+
prefix,
|
|
10870
|
+
prefixWidth
|
|
10578
10871
|
})
|
|
10579
10872
|
);
|
|
10580
|
-
if (!decorated.every((line) =>
|
|
10581
|
-
if (!nativeLines.every((line) =>
|
|
10873
|
+
if (!decorated.every((line) => visibleWidth(line) <= width)) return void 0;
|
|
10874
|
+
if (!nativeLines.every((line) => getLineAnalysis(line).visibleWidth <= bodyWidth)) return void 0;
|
|
10582
10875
|
return decorated;
|
|
10583
10876
|
}
|
|
10584
10877
|
function decorateMessageRender(original, instance, args, snapshot = {
|
|
@@ -10591,10 +10884,20 @@ function decorateMessageRender(original, instance, args, snapshot = {
|
|
|
10591
10884
|
const width = typeof args[0] === "number" ? args[0] : 0;
|
|
10592
10885
|
const prefix = snapshot.assistantPrefix;
|
|
10593
10886
|
if (!snapshot.assistantEnabled) return Reflect.apply(original, instance, args);
|
|
10594
|
-
|
|
10595
|
-
|
|
10887
|
+
const prefixWidth = visibleWidth(prefix);
|
|
10888
|
+
if (width <= prefixWidth) return Reflect.apply(original, instance, args);
|
|
10889
|
+
const reducedWidth = width - prefixWidth;
|
|
10596
10890
|
const native = Reflect.apply(original, instance, [reducedWidth, ...args.slice(1)]);
|
|
10597
|
-
|
|
10891
|
+
if (!Array.isArray(native) || !native.every((line) => typeof line === "string")) return native;
|
|
10892
|
+
const cached = getRenderCache(instance).get(cacheKey(width, prefix));
|
|
10893
|
+
if (cached && (cached.nativeRef === native || sameLines(cached.nativeLines, native))) {
|
|
10894
|
+
messageDecorationTestState.cacheHits++;
|
|
10895
|
+
return [...cached.result];
|
|
10896
|
+
}
|
|
10897
|
+
messageDecorationTestState.cacheMisses++;
|
|
10898
|
+
const decorated = prefixNative(native, width, prefix) ?? native;
|
|
10899
|
+
storeRenderCache(instance, width, prefix, native, decorated);
|
|
10900
|
+
return decorated;
|
|
10598
10901
|
}
|
|
10599
10902
|
function isSpacerChild(child) {
|
|
10600
10903
|
return typeof child?.setLines === "function";
|
|
@@ -10954,7 +11257,7 @@ var KNOWN_NATIVE_IDENTITIES = Object.freeze({
|
|
|
10954
11257
|
});
|
|
10955
11258
|
var TRUSTED_NATIVE_FINGERPRINTS = Object.freeze(
|
|
10956
11259
|
Object.fromEntries(
|
|
10957
|
-
Object.entries(KNOWN_NATIVE_IDENTITIES).map(([key, identities]) => [key, identities[0]
|
|
11260
|
+
Object.entries(KNOWN_NATIVE_IDENTITIES).map(([key, identities]) => [key, identities[0]?.fingerprint ?? ""])
|
|
10958
11261
|
)
|
|
10959
11262
|
);
|
|
10960
11263
|
function fingerprint(value) {
|
|
@@ -11810,22 +12113,134 @@ function createPiStyleSessionCoordinator(pi, hooks = {}) {
|
|
|
11810
12113
|
}
|
|
11811
12114
|
|
|
11812
12115
|
// extension-src/pi-style/pi/session-usage.ts
|
|
12116
|
+
var usageCache = /* @__PURE__ */ new WeakMap();
|
|
11813
12117
|
function usageFromSession(session) {
|
|
11814
|
-
const
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
12118
|
+
const entries = session.getEntries();
|
|
12119
|
+
const state = usageCache.get(session) ?? createState();
|
|
12120
|
+
usageCache.set(session, state);
|
|
12121
|
+
if (entries.length === 0) {
|
|
12122
|
+
state.cacheHits++;
|
|
12123
|
+
state.processedLength = 0;
|
|
12124
|
+
state.lastEntryId = void 0;
|
|
12125
|
+
state.lastEntryRef = void 0;
|
|
12126
|
+
state.lastContribution = emptyContribution();
|
|
12127
|
+
state.totals = emptyTotals();
|
|
12128
|
+
state.cached = void 0;
|
|
12129
|
+
return void 0;
|
|
12130
|
+
}
|
|
12131
|
+
if (state.processedLength === 0) {
|
|
12132
|
+
rebuildFrom(entries, state);
|
|
12133
|
+
return state.cached;
|
|
12134
|
+
}
|
|
12135
|
+
if (entries.length < state.processedLength) {
|
|
12136
|
+
rebuildFrom(entries, state);
|
|
12137
|
+
return state.cached;
|
|
12138
|
+
}
|
|
12139
|
+
const currentLast = entries.at(-1);
|
|
12140
|
+
if (!currentLast) {
|
|
12141
|
+
state.cacheHits++;
|
|
12142
|
+
state.cached = void 0;
|
|
12143
|
+
return void 0;
|
|
12144
|
+
}
|
|
12145
|
+
if (entries.length === state.processedLength) {
|
|
12146
|
+
if (currentLast.id !== state.lastEntryId) {
|
|
12147
|
+
rebuildFrom(entries, state);
|
|
12148
|
+
return state.cached;
|
|
12149
|
+
}
|
|
12150
|
+
if (currentLast !== state.lastEntryRef) refreshTailEntry(currentLast, state);
|
|
12151
|
+
else state.cacheHits++;
|
|
12152
|
+
return state.cached;
|
|
12153
|
+
}
|
|
12154
|
+
const previousLast = entries[state.processedLength - 1];
|
|
12155
|
+
if (!previousLast || previousLast.id !== state.lastEntryId) {
|
|
12156
|
+
rebuildFrom(entries, state);
|
|
12157
|
+
return state.cached;
|
|
12158
|
+
}
|
|
12159
|
+
if (previousLast !== state.lastEntryRef) refreshTailEntry(previousLast, state);
|
|
12160
|
+
for (const entry of entries.slice(state.processedLength)) appendEntry(entry, state);
|
|
12161
|
+
return state.cached;
|
|
12162
|
+
}
|
|
12163
|
+
function resetUsageFromSessionCache(session) {
|
|
12164
|
+
if (session) {
|
|
12165
|
+
usageCache.delete(session);
|
|
12166
|
+
return;
|
|
12167
|
+
}
|
|
12168
|
+
usageCache = /* @__PURE__ */ new WeakMap();
|
|
12169
|
+
}
|
|
12170
|
+
function createState() {
|
|
12171
|
+
return {
|
|
12172
|
+
processedLength: 0,
|
|
12173
|
+
lastEntryId: void 0,
|
|
12174
|
+
lastEntryRef: void 0,
|
|
12175
|
+
lastContribution: emptyContribution(),
|
|
12176
|
+
totals: emptyTotals(),
|
|
12177
|
+
cached: void 0,
|
|
12178
|
+
scannedEntries: 0,
|
|
12179
|
+
cacheHits: 0,
|
|
12180
|
+
rebuilds: 0,
|
|
12181
|
+
tailRefreshes: 0
|
|
12182
|
+
};
|
|
12183
|
+
}
|
|
12184
|
+
function rebuildFrom(entries, state) {
|
|
12185
|
+
state.rebuilds++;
|
|
12186
|
+
state.processedLength = 0;
|
|
12187
|
+
state.lastEntryId = void 0;
|
|
12188
|
+
state.lastEntryRef = void 0;
|
|
12189
|
+
state.lastContribution = emptyContribution();
|
|
12190
|
+
state.totals = emptyTotals();
|
|
12191
|
+
state.cached = void 0;
|
|
12192
|
+
for (const entry of entries) appendEntry(entry, state);
|
|
12193
|
+
}
|
|
12194
|
+
function refreshTailEntry(entry, state) {
|
|
12195
|
+
state.tailRefreshes++;
|
|
12196
|
+
subtractContribution(state.totals, state.lastContribution);
|
|
12197
|
+
const contribution = usageContribution(entry);
|
|
12198
|
+
addContribution(state.totals, contribution);
|
|
12199
|
+
state.lastEntryId = entry.id;
|
|
12200
|
+
state.lastEntryRef = entry;
|
|
12201
|
+
state.lastContribution = contribution;
|
|
12202
|
+
state.cached = snapshotFromTotals(state.totals);
|
|
12203
|
+
state.scannedEntries++;
|
|
12204
|
+
}
|
|
12205
|
+
function appendEntry(entry, state) {
|
|
12206
|
+
const contribution = usageContribution(entry);
|
|
12207
|
+
addContribution(state.totals, contribution);
|
|
12208
|
+
state.processedLength++;
|
|
12209
|
+
state.lastEntryId = entry.id;
|
|
12210
|
+
state.lastEntryRef = entry;
|
|
12211
|
+
state.lastContribution = contribution;
|
|
12212
|
+
state.cached = snapshotFromTotals(state.totals);
|
|
12213
|
+
state.scannedEntries++;
|
|
12214
|
+
}
|
|
12215
|
+
function usageContribution(entry) {
|
|
12216
|
+
if (entry.type === "message") {
|
|
12217
|
+
if (entry.message.role !== "assistant" && entry.message.role !== "toolResult") return emptyContribution();
|
|
12218
|
+
const usage = "usage" in entry.message ? entry.message.usage : void 0;
|
|
12219
|
+
if (!usage) return emptyContribution();
|
|
12220
|
+
return {
|
|
12221
|
+
input: usage.input,
|
|
12222
|
+
output: usage.output,
|
|
12223
|
+
cacheRead: usage.cacheRead,
|
|
12224
|
+
cacheWrite: usage.cacheWrite,
|
|
12225
|
+
cost: usage.cost.total,
|
|
12226
|
+
sawUsage: true
|
|
12227
|
+
};
|
|
12228
|
+
}
|
|
12229
|
+
if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
|
|
12230
|
+
return {
|
|
12231
|
+
input: entry.usage.input,
|
|
12232
|
+
output: entry.usage.output,
|
|
12233
|
+
cacheRead: entry.usage.cacheRead,
|
|
12234
|
+
cacheWrite: entry.usage.cacheWrite,
|
|
12235
|
+
cost: entry.usage.cost.total,
|
|
12236
|
+
sawUsage: true
|
|
12237
|
+
};
|
|
12238
|
+
}
|
|
12239
|
+
return emptyContribution();
|
|
12240
|
+
}
|
|
12241
|
+
function snapshotFromTotals(totals) {
|
|
12242
|
+
if (totals.input === 0 && totals.output === 0 && totals.cacheRead === 0 && totals.cacheWrite === 0 && totals.cost === 0)
|
|
12243
|
+
return void 0;
|
|
11829
12244
|
return {
|
|
11830
12245
|
inputTokens: totals.input,
|
|
11831
12246
|
outputTokens: totals.output,
|
|
@@ -11836,18 +12251,30 @@ function usageFromSession(session) {
|
|
|
11836
12251
|
streaming: false
|
|
11837
12252
|
};
|
|
11838
12253
|
}
|
|
11839
|
-
function
|
|
12254
|
+
function addContribution(totals, usage) {
|
|
11840
12255
|
totals.input += usage.input;
|
|
11841
12256
|
totals.output += usage.output;
|
|
11842
12257
|
totals.cacheRead += usage.cacheRead;
|
|
11843
12258
|
totals.cacheWrite += usage.cacheWrite;
|
|
11844
|
-
totals.cost += usage.cost
|
|
12259
|
+
totals.cost += usage.cost;
|
|
12260
|
+
}
|
|
12261
|
+
function subtractContribution(totals, usage) {
|
|
12262
|
+
totals.input -= usage.input;
|
|
12263
|
+
totals.output -= usage.output;
|
|
12264
|
+
totals.cacheRead -= usage.cacheRead;
|
|
12265
|
+
totals.cacheWrite -= usage.cacheWrite;
|
|
12266
|
+
totals.cost -= usage.cost;
|
|
12267
|
+
}
|
|
12268
|
+
function emptyTotals() {
|
|
12269
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
12270
|
+
}
|
|
12271
|
+
function emptyContribution() {
|
|
12272
|
+
return { ...emptyTotals(), sawUsage: false };
|
|
11845
12273
|
}
|
|
11846
12274
|
|
|
11847
12275
|
// extension-src/pi-style/pi/index.ts
|
|
11848
12276
|
function usagePatch(ctx) {
|
|
11849
|
-
|
|
11850
|
-
return usage ? { usage } : {};
|
|
12277
|
+
return { usage: usageFromSession(ctx.sessionManager) };
|
|
11851
12278
|
}
|
|
11852
12279
|
function activateReadOnlyTools(pi) {
|
|
11853
12280
|
const available = new Set(pi.getAllTools().map((tool) => tool.name));
|
|
@@ -11882,6 +12309,7 @@ function piStyleExtension(pi) {
|
|
|
11882
12309
|
const coordinator = createPiStyleSessionCoordinator(pi, compatibilityTestHooks);
|
|
11883
12310
|
registerPiStyleCommand(pi, coordinator.app);
|
|
11884
12311
|
pi.on("session_start", async (event, ctx) => {
|
|
12312
|
+
resetUsageFromSessionCache(ctx.sessionManager);
|
|
11885
12313
|
if (pi.getFlag("pi-style-readonly-tools") === true) {
|
|
11886
12314
|
activateReadOnlyTools(pi);
|
|
11887
12315
|
}
|
|
@@ -11915,15 +12343,21 @@ function piStyleExtension(pi) {
|
|
|
11915
12343
|
)
|
|
11916
12344
|
);
|
|
11917
12345
|
pi.on("thinking_level_select", (event) => coordinator.app.update({ thinkingLevel: event.level }, "immediate"));
|
|
11918
|
-
pi.on(
|
|
12346
|
+
pi.on(
|
|
12347
|
+
"session_info_changed",
|
|
12348
|
+
(event) => coordinator.app.update({ sessionName: event.name }, "coalesced", { refreshExtensionStatuses: true })
|
|
12349
|
+
);
|
|
11919
12350
|
pi.on("message_start", () => {
|
|
11920
12351
|
closeActiveBatch();
|
|
11921
12352
|
});
|
|
11922
12353
|
pi.on("message_update", () => coordinator.app.update({}, "coalesced"));
|
|
11923
|
-
pi.on(
|
|
12354
|
+
pi.on(
|
|
12355
|
+
"message_end",
|
|
12356
|
+
(_event, ctx) => coordinator.app.update({ ...usagePatch(ctx) }, "coalesced", { refreshContextUsage: true })
|
|
12357
|
+
);
|
|
11924
12358
|
pi.on("turn_end", (event, ctx) => {
|
|
11925
12359
|
registerTurnFromMessage(event.message, event.toolResults);
|
|
11926
|
-
coordinator.app.update({ ...usagePatch(ctx) }, "deferred");
|
|
12360
|
+
coordinator.app.update({ ...usagePatch(ctx) }, "deferred", { refreshContextUsage: true });
|
|
11927
12361
|
});
|
|
11928
12362
|
pi.on("agent_end", () => {
|
|
11929
12363
|
const run = finishAgentRun();
|
|
@@ -11932,23 +12366,33 @@ function piStyleExtension(pi) {
|
|
|
11932
12366
|
requestToolPresentationRender();
|
|
11933
12367
|
}
|
|
11934
12368
|
});
|
|
11935
|
-
pi.on(
|
|
12369
|
+
pi.on(
|
|
12370
|
+
"agent_settled",
|
|
12371
|
+
(_event, ctx) => coordinator.app.update({ ...usagePatch(ctx) }, "coalesced", { refreshContextUsage: true })
|
|
12372
|
+
);
|
|
11936
12373
|
pi.on("session_tree", (_event, ctx) => {
|
|
12374
|
+
resetUsageFromSessionCache(ctx.sessionManager);
|
|
11937
12375
|
rebuildTurnRegistryFromEntries(ctx.sessionManager.getEntries());
|
|
11938
|
-
coordinator.app.update({ ...usagePatch(ctx) }, "deferred");
|
|
12376
|
+
coordinator.app.update({ ...usagePatch(ctx) }, "deferred", { refreshContextUsage: true });
|
|
12377
|
+
});
|
|
12378
|
+
pi.on("session_compact", (_event, ctx) => {
|
|
12379
|
+
resetUsageFromSessionCache(ctx.sessionManager);
|
|
12380
|
+
coordinator.app.update({ ...usagePatch(ctx) }, "deferred", { refreshContextUsage: true });
|
|
11939
12381
|
});
|
|
11940
|
-
pi.on("session_compact", (_event, ctx) => coordinator.app.update({ ...usagePatch(ctx) }, "deferred"));
|
|
11941
12382
|
pi.on("tool_result", (event, ctx) => {
|
|
11942
12383
|
if (["write", "edit", "bash"].includes(event.toolName)) {
|
|
11943
12384
|
coordinator.app.runtime.current?.invalidateGit();
|
|
11944
|
-
coordinator.app.update({ ...usagePatch(ctx) }, "delayed-retry");
|
|
12385
|
+
coordinator.app.update({ ...usagePatch(ctx) }, "delayed-retry", { refreshContextUsage: true });
|
|
11945
12386
|
}
|
|
11946
12387
|
});
|
|
11947
12388
|
pi.on("user_bash", (_event, ctx) => {
|
|
11948
12389
|
coordinator.app.runtime.current?.invalidateGit();
|
|
11949
|
-
coordinator.app.update({ ...usagePatch(ctx) }, "delayed-retry");
|
|
12390
|
+
coordinator.app.update({ ...usagePatch(ctx) }, "delayed-retry", { refreshContextUsage: true });
|
|
12391
|
+
});
|
|
12392
|
+
pi.on("session_shutdown", (_event, ctx) => {
|
|
12393
|
+
resetUsageFromSessionCache(ctx.sessionManager);
|
|
12394
|
+
coordinator.shutdown();
|
|
11950
12395
|
});
|
|
11951
|
-
pi.on("session_shutdown", () => coordinator.shutdown());
|
|
11952
12396
|
}
|
|
11953
12397
|
export {
|
|
11954
12398
|
__setCompatibilityTestHooks,
|