zidane 5.1.13 → 5.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-skiQGYs2.d.ts → agent-BuGxYfqh.d.ts} +11 -4
- package/dist/agent-BuGxYfqh.d.ts.map +1 -0
- package/dist/chat.d.ts +172 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/{index-CjPh6CRE.d.ts → index-Aaa1tP6E.d.ts} +2 -2
- package/dist/{index-CjPh6CRE.d.ts.map → index-Aaa1tP6E.d.ts.map} +1 -1
- package/dist/{index-YM7SipFz.d.ts → index-Cv5wED8j.d.ts} +2 -2
- package/dist/{index-YM7SipFz.d.ts.map → index-Cv5wED8j.d.ts.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/{login-Cc6Q-Fpu.js → login-DJscx_sS.js} +4 -4
- package/dist/{login-Cc6Q-Fpu.js.map → login-DJscx_sS.js.map} +1 -1
- package/dist/{mcp-CUt-N8zn.js → mcp-Bq_rD6e9.js} +2 -2
- package/dist/{mcp-CUt-N8zn.js.map → mcp-Bq_rD6e9.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{presets-Ce79MK4J.js → presets-BEruW0Ji.js} +2 -2
- package/dist/{presets-Ce79MK4J.js.map → presets-BEruW0Ji.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/{tools-BG2wMa3X.js → tools-BBFu1UsV.js} +3 -3
- package/dist/{tools-BG2wMa3X.js.map → tools-BBFu1UsV.js.map} +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{tool-formatters-0aOMYbH-.d.ts → transcript-anchors-FJMZyLS4.d.ts} +242 -97
- package/dist/transcript-anchors-FJMZyLS4.d.ts.map +1 -0
- package/dist/tui.d.ts +55 -39
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +335 -314
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CDmQ2h-T.js → turn-operations-CeLlc7jt.js} +572 -91
- package/dist/turn-operations-CeLlc7jt.js.map +1 -0
- package/dist/{types-Bx_F8jet.js → types-IcokUOyC.js} +11 -4
- package/dist/{types-Bx_F8jet.js.map → types-IcokUOyC.js.map} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/dist/agent-skiQGYs2.d.ts.map +0 -1
- package/dist/tool-formatters-0aOMYbH-.d.ts.map +0 -1
- package/dist/turn-operations-CDmQ2h-T.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as multiEdit, c as grep, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, r as shell, t as writeFile$1, u as edit } from "./tools-
|
|
2
|
-
import { n as toolResultToText } from "./types-
|
|
3
|
-
import { r as normalizeMcpServers } from "./mcp-
|
|
1
|
+
import { a as multiEdit, c as grep, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, r as shell, t as writeFile$1, u as edit } from "./tools-BBFu1UsV.js";
|
|
2
|
+
import { n as toolResultToText } from "./types-IcokUOyC.js";
|
|
3
|
+
import { r as normalizeMcpServers } from "./mcp-Bq_rD6e9.js";
|
|
4
4
|
import { a as discoverSkills } from "./interpolate-BI6ovwag.js";
|
|
5
5
|
import { n as formatTokenUsage } from "./stats-DgOvY7wd.js";
|
|
6
|
-
import { n as definePreset } from "./presets-
|
|
6
|
+
import { n as definePreset } from "./presets-BEruW0Ji.js";
|
|
7
7
|
import { i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-CvriFHFU.js";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
import { readdir, stat, writeFile } from "node:fs/promises";
|
|
@@ -14,6 +14,7 @@ import { homedir } from "node:os";
|
|
|
14
14
|
import { anthropicOAuthProvider, openaiCodexOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
|
15
15
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
16
16
|
import { jsx } from "react/jsx-runtime";
|
|
17
|
+
import { jsx as jsx$1 } from "@opentui/react/jsx-runtime";
|
|
17
18
|
//#region src/chat/agent-prompt.ts
|
|
18
19
|
/**
|
|
19
20
|
* Agent system-prompt fragments — composable doctrine for built-in profiles.
|
|
@@ -848,6 +849,43 @@ function shouldAutoCompact(input) {
|
|
|
848
849
|
};
|
|
849
850
|
}
|
|
850
851
|
//#endregion
|
|
852
|
+
//#region src/chat/boot-profiler.ts
|
|
853
|
+
const enabled = !!process.env.ZIDANE_BOOT_PROFILE;
|
|
854
|
+
/**
|
|
855
|
+
* High-resolution origin for the running profile. Captured at module
|
|
856
|
+
* load time so the first {@link bootTick} call inside the same process
|
|
857
|
+
* reflects "time since the boot profiler was first reached", which for
|
|
858
|
+
* the standard `cli.ts → runTui` path is effectively "time since the
|
|
859
|
+
* TUI binary started executing user code".
|
|
860
|
+
*/
|
|
861
|
+
const start = performance.now();
|
|
862
|
+
let last = start;
|
|
863
|
+
/**
|
|
864
|
+
* Record a checkpoint. No-op unless `ZIDANE_BOOT_PROFILE` is truthy in
|
|
865
|
+
* the environment.
|
|
866
|
+
*
|
|
867
|
+
* The leading delta is the time since the PREVIOUS tick (so a long
|
|
868
|
+
* delta highlights the immediately-preceding work); the trailing total
|
|
869
|
+
* is the time since the profiler started. Both round to one decimal of
|
|
870
|
+
* a millisecond.
|
|
871
|
+
*/
|
|
872
|
+
function bootTick(label) {
|
|
873
|
+
if (!enabled) return;
|
|
874
|
+
const now = performance.now();
|
|
875
|
+
const delta = now - last;
|
|
876
|
+
const total = now - start;
|
|
877
|
+
last = now;
|
|
878
|
+
process.stderr.write(`[boot] +${delta.toFixed(1).padStart(7)}ms / total ${total.toFixed(1).padStart(7)}ms / ${label}\n`);
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Returns `true` when `ZIDANE_BOOT_PROFILE` is set. Useful for guarding
|
|
882
|
+
* heavier instrumentation (e.g. wrapping a costly call in a span) that
|
|
883
|
+
* you don't want paying its own cost in the default path.
|
|
884
|
+
*/
|
|
885
|
+
function bootProfileEnabled() {
|
|
886
|
+
return enabled;
|
|
887
|
+
}
|
|
888
|
+
//#endregion
|
|
851
889
|
//#region src/chat/browser.ts
|
|
852
890
|
/**
|
|
853
891
|
* Best-effort cross-platform browser open.
|
|
@@ -887,6 +925,95 @@ function tryOpenBrowser(url) {
|
|
|
887
925
|
} catch {}
|
|
888
926
|
}
|
|
889
927
|
//#endregion
|
|
928
|
+
//#region src/chat/color-gradient.ts
|
|
929
|
+
/** Parse `#rrggbb` (case-insensitive) into `[r, g, b]` 0–255 integers. */
|
|
930
|
+
function parseHex(hex) {
|
|
931
|
+
const h = hex.replace("#", "");
|
|
932
|
+
return [
|
|
933
|
+
Number.parseInt(h.slice(0, 2), 16),
|
|
934
|
+
Number.parseInt(h.slice(2, 4), 16),
|
|
935
|
+
Number.parseInt(h.slice(4, 6), 16)
|
|
936
|
+
];
|
|
937
|
+
}
|
|
938
|
+
/** Convert sRGB 0–255 → HSL 0–1. */
|
|
939
|
+
function rgbToHsl(r, g, b) {
|
|
940
|
+
r /= 255;
|
|
941
|
+
g /= 255;
|
|
942
|
+
b /= 255;
|
|
943
|
+
const max = Math.max(r, g, b);
|
|
944
|
+
const min = Math.min(r, g, b);
|
|
945
|
+
const l = (max + min) / 2;
|
|
946
|
+
if (max === min) return [
|
|
947
|
+
0,
|
|
948
|
+
0,
|
|
949
|
+
l
|
|
950
|
+
];
|
|
951
|
+
const d = max - min;
|
|
952
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
953
|
+
let h;
|
|
954
|
+
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
|
|
955
|
+
else if (max === g) h = (b - r) / d + 2;
|
|
956
|
+
else h = (r - g) / d + 4;
|
|
957
|
+
return [
|
|
958
|
+
h / 6,
|
|
959
|
+
s,
|
|
960
|
+
l
|
|
961
|
+
];
|
|
962
|
+
}
|
|
963
|
+
/** Convert HSL 0–1 → sRGB 0–255. Standard piecewise formula. */
|
|
964
|
+
function hslToRgb(h, s, l) {
|
|
965
|
+
if (s === 0) return [
|
|
966
|
+
l * 255,
|
|
967
|
+
l * 255,
|
|
968
|
+
l * 255
|
|
969
|
+
];
|
|
970
|
+
const hue2rgb = (p, q, t) => {
|
|
971
|
+
if (t < 0) t += 1;
|
|
972
|
+
if (t > 1) t -= 1;
|
|
973
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
974
|
+
if (t < 1 / 2) return q;
|
|
975
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
976
|
+
return p;
|
|
977
|
+
};
|
|
978
|
+
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
979
|
+
const p = 2 * l - q;
|
|
980
|
+
return [
|
|
981
|
+
hue2rgb(p, q, h + 1 / 3) * 255,
|
|
982
|
+
hue2rgb(p, q, h) * 255,
|
|
983
|
+
hue2rgb(p, q, h - 1 / 3) * 255
|
|
984
|
+
];
|
|
985
|
+
}
|
|
986
|
+
function toHex(rgb) {
|
|
987
|
+
const pad = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
|
|
988
|
+
return `#${pad(rgb[0])}${pad(rgb[1])}${pad(rgb[2])}`;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Blend two hex colors in HSL space with shortest-path hue interpolation.
|
|
992
|
+
* `t` ∈ [0, 1]; `t=0` returns `from`, `t=1` returns `to`.
|
|
993
|
+
*/
|
|
994
|
+
function blendHsl(from, to, t) {
|
|
995
|
+
const [r1, g1, b1] = parseHex(from);
|
|
996
|
+
const [r2, g2, b2] = parseHex(to);
|
|
997
|
+
const [h1, s1, l1] = rgbToHsl(r1, g1, b1);
|
|
998
|
+
const [h2, s2, l2] = rgbToHsl(r2, g2, b2);
|
|
999
|
+
let dh = h2 - h1;
|
|
1000
|
+
if (dh > .5) dh -= 1;
|
|
1001
|
+
else if (dh < -.5) dh += 1;
|
|
1002
|
+
return toHex(hslToRgb((h1 + dh * t + 1) % 1, s1 + (s2 - s1) * t, l1 + (l2 - l1) * t));
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Static gradient ramp of length `n` going from `from` (index 0) to
|
|
1006
|
+
* `to` (index n-1) in HSL space. For the cycling A→B→A→B ramp the
|
|
1007
|
+
* throbber uses, see `buildCycleRamp` in `src/tui/crush-throbber.tsx`.
|
|
1008
|
+
*/
|
|
1009
|
+
function buildLinearRamp(from, to, n) {
|
|
1010
|
+
if (n <= 0) return [];
|
|
1011
|
+
if (n === 1) return [blendHsl(from, to, .5)];
|
|
1012
|
+
const ramp = [];
|
|
1013
|
+
for (let i = 0; i < n; i++) ramp.push(blendHsl(from, to, i / (n - 1)));
|
|
1014
|
+
return ramp;
|
|
1015
|
+
}
|
|
1016
|
+
//#endregion
|
|
890
1017
|
//#region src/chat/completion.ts
|
|
891
1018
|
/**
|
|
892
1019
|
* Prompt autocompletion framework.
|
|
@@ -1117,6 +1244,66 @@ const FILES_TRIGGER = "@";
|
|
|
1117
1244
|
/** Cap on returned items. Keeps the popover compact + render-cheap. */
|
|
1118
1245
|
const DEFAULT_RESULT_LIMIT = 50;
|
|
1119
1246
|
/**
|
|
1247
|
+
* Rank-and-slice a file catalog against a query. Hoisted to a module
|
|
1248
|
+
* helper so both the sync and async branches of `suggest()` share one
|
|
1249
|
+
* implementation (the async branch hits this once the lazy directory
|
|
1250
|
+
* walk resolves; sync branch hits it on every keystroke thereafter).
|
|
1251
|
+
*/
|
|
1252
|
+
function scoreFiles(catalog, query, limit) {
|
|
1253
|
+
const q = query.trim().toLowerCase();
|
|
1254
|
+
const scored = [];
|
|
1255
|
+
for (const file of catalog) {
|
|
1256
|
+
const name = file.name.toLowerCase();
|
|
1257
|
+
const path = file.path.toLowerCase();
|
|
1258
|
+
if (q.length === 0) {
|
|
1259
|
+
scored.push({
|
|
1260
|
+
entry: file,
|
|
1261
|
+
rank: 4
|
|
1262
|
+
});
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
if (name === q) {
|
|
1266
|
+
scored.push({
|
|
1267
|
+
entry: file,
|
|
1268
|
+
rank: 0
|
|
1269
|
+
});
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (name.startsWith(q)) {
|
|
1273
|
+
scored.push({
|
|
1274
|
+
entry: file,
|
|
1275
|
+
rank: 1
|
|
1276
|
+
});
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (name.includes(q)) {
|
|
1280
|
+
scored.push({
|
|
1281
|
+
entry: file,
|
|
1282
|
+
rank: 2
|
|
1283
|
+
});
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
if (path.includes(q)) {
|
|
1287
|
+
scored.push({
|
|
1288
|
+
entry: file,
|
|
1289
|
+
rank: 3
|
|
1290
|
+
});
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
scored.sort((a, b) => {
|
|
1295
|
+
if (a.rank !== b.rank) return a.rank - b.rank;
|
|
1296
|
+
return a.entry.path.localeCompare(b.entry.path);
|
|
1297
|
+
});
|
|
1298
|
+
return scored.slice(0, limit).map(({ entry }) => ({
|
|
1299
|
+
id: entry.path,
|
|
1300
|
+
label: entry.name,
|
|
1301
|
+
description: parentDir(entry.path),
|
|
1302
|
+
insertText: `@${entry.path} `,
|
|
1303
|
+
data: entry
|
|
1304
|
+
}));
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1120
1307
|
* Build an `@`-prefixed files completion provider against a *live* catalog.
|
|
1121
1308
|
*
|
|
1122
1309
|
* The factory captures a getter so the catalog can be re-scanned (cwd
|
|
@@ -1136,59 +1323,11 @@ function createFilesCompletionProvider(opts) {
|
|
|
1136
1323
|
trigger: "@",
|
|
1137
1324
|
label: "Files",
|
|
1138
1325
|
suggest(query) {
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
for (const file of catalog) {
|
|
1143
|
-
const name = file.name.toLowerCase();
|
|
1144
|
-
const path = file.path.toLowerCase();
|
|
1145
|
-
if (q.length === 0) {
|
|
1146
|
-
scored.push({
|
|
1147
|
-
entry: file,
|
|
1148
|
-
rank: 4
|
|
1149
|
-
});
|
|
1150
|
-
continue;
|
|
1151
|
-
}
|
|
1152
|
-
if (name === q) {
|
|
1153
|
-
scored.push({
|
|
1154
|
-
entry: file,
|
|
1155
|
-
rank: 0
|
|
1156
|
-
});
|
|
1157
|
-
continue;
|
|
1158
|
-
}
|
|
1159
|
-
if (name.startsWith(q)) {
|
|
1160
|
-
scored.push({
|
|
1161
|
-
entry: file,
|
|
1162
|
-
rank: 1
|
|
1163
|
-
});
|
|
1164
|
-
continue;
|
|
1165
|
-
}
|
|
1166
|
-
if (name.includes(q)) {
|
|
1167
|
-
scored.push({
|
|
1168
|
-
entry: file,
|
|
1169
|
-
rank: 2
|
|
1170
|
-
});
|
|
1171
|
-
continue;
|
|
1172
|
-
}
|
|
1173
|
-
if (path.includes(q)) {
|
|
1174
|
-
scored.push({
|
|
1175
|
-
entry: file,
|
|
1176
|
-
rank: 3
|
|
1177
|
-
});
|
|
1178
|
-
continue;
|
|
1179
|
-
}
|
|
1326
|
+
if (opts.ensureCatalog) {
|
|
1327
|
+
const pending = opts.ensureCatalog();
|
|
1328
|
+
if (opts.getCatalog().length === 0) return pending.then((loaded) => scoreFiles(loaded, query, limit));
|
|
1180
1329
|
}
|
|
1181
|
-
|
|
1182
|
-
if (a.rank !== b.rank) return a.rank - b.rank;
|
|
1183
|
-
return a.entry.path.localeCompare(b.entry.path);
|
|
1184
|
-
});
|
|
1185
|
-
return scored.slice(0, limit).map(({ entry }) => ({
|
|
1186
|
-
id: entry.path,
|
|
1187
|
-
label: entry.name,
|
|
1188
|
-
description: parentDir(entry.path),
|
|
1189
|
-
insertText: `@${entry.path} `,
|
|
1190
|
-
data: entry
|
|
1191
|
-
}));
|
|
1330
|
+
return scoreFiles(opts.getCatalog(), query, limit);
|
|
1192
1331
|
},
|
|
1193
1332
|
parseReferences(text, _ctx) {
|
|
1194
1333
|
const catalog = opts.getCatalog();
|
|
@@ -1245,6 +1384,33 @@ const SKILLS_TRIGGER = "/";
|
|
|
1245
1384
|
/** Valid skill-name shape (matches the parser): lowercase alnum + dashes. */
|
|
1246
1385
|
const SKILL_NAME_RX = /^[a-z0-9][a-z0-9-]*$/;
|
|
1247
1386
|
/**
|
|
1387
|
+
* Filter + rank visible skills against a query. Hoisted to a module
|
|
1388
|
+
* helper so the sync and async branches of `suggest()` share one
|
|
1389
|
+
* implementation (the async branch hits this once the lazy SKILL.md
|
|
1390
|
+
* scan resolves; sync branch hits it on every keystroke thereafter).
|
|
1391
|
+
*/
|
|
1392
|
+
function scoreSkills(catalog, query) {
|
|
1393
|
+
const q = query.trim().toLowerCase();
|
|
1394
|
+
return catalog.filter((skill) => SKILL_NAME_RX.test(skill.name)).filter((skill) => {
|
|
1395
|
+
if (q.length === 0) return true;
|
|
1396
|
+
return skill.name.toLowerCase().includes(q) || skill.description.toLowerCase().includes(q);
|
|
1397
|
+
}).sort((a, b) => {
|
|
1398
|
+
const an = a.name.toLowerCase();
|
|
1399
|
+
const bn = b.name.toLowerCase();
|
|
1400
|
+
if (q) {
|
|
1401
|
+
const aPrefix = an.startsWith(q);
|
|
1402
|
+
if (aPrefix !== bn.startsWith(q)) return aPrefix ? -1 : 1;
|
|
1403
|
+
}
|
|
1404
|
+
return an.localeCompare(bn);
|
|
1405
|
+
}).map((skill) => ({
|
|
1406
|
+
id: skill.name,
|
|
1407
|
+
label: skill.name,
|
|
1408
|
+
description: skill.description,
|
|
1409
|
+
insertText: `/${skill.name} `,
|
|
1410
|
+
data: skill
|
|
1411
|
+
}));
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1248
1414
|
* Build a slash-command completion provider against a *live* skills
|
|
1249
1415
|
* catalog. The factory captures a getter so the catalog can change across
|
|
1250
1416
|
* renders (toggles, reload) without re-instantiating the provider.
|
|
@@ -1265,25 +1431,11 @@ function createSkillsCompletionProvider(opts) {
|
|
|
1265
1431
|
trigger: "/",
|
|
1266
1432
|
label: "Skills",
|
|
1267
1433
|
suggest(query) {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if (
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
const an = a.name.toLowerCase();
|
|
1274
|
-
const bn = b.name.toLowerCase();
|
|
1275
|
-
if (q) {
|
|
1276
|
-
const aPrefix = an.startsWith(q);
|
|
1277
|
-
if (aPrefix !== bn.startsWith(q)) return aPrefix ? -1 : 1;
|
|
1278
|
-
}
|
|
1279
|
-
return an.localeCompare(bn);
|
|
1280
|
-
}).map((skill) => ({
|
|
1281
|
-
id: skill.name,
|
|
1282
|
-
label: skill.name,
|
|
1283
|
-
description: skill.description,
|
|
1284
|
-
insertText: `/${skill.name} `,
|
|
1285
|
-
data: skill
|
|
1286
|
-
}));
|
|
1434
|
+
if (opts.ensureCatalog) {
|
|
1435
|
+
const pending = opts.ensureCatalog();
|
|
1436
|
+
if (opts.getCatalog().length === 0) return pending.then(() => scoreSkills(visible(), query));
|
|
1437
|
+
}
|
|
1438
|
+
return scoreSkills(visible(), query);
|
|
1287
1439
|
},
|
|
1288
1440
|
parseReferences(text, _ctx) {
|
|
1289
1441
|
const catalog = visible();
|
|
@@ -2803,6 +2955,98 @@ function useConfig() {
|
|
|
2803
2955
|
return ctx;
|
|
2804
2956
|
}
|
|
2805
2957
|
//#endregion
|
|
2958
|
+
//#region src/chat/discovery-context.tsx
|
|
2959
|
+
const DiscoveryContext = createContext(null);
|
|
2960
|
+
function DiscoveryProvider({ value, children }) {
|
|
2961
|
+
return /* @__PURE__ */ jsx$1(DiscoveryContext.Provider, {
|
|
2962
|
+
value,
|
|
2963
|
+
children
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
/**
|
|
2967
|
+
* Read live discovery state + actions. Throws if used outside a
|
|
2968
|
+
* `<DiscoveryProvider>` — discovery is a load-bearing dependency for
|
|
2969
|
+
* settings + completion popovers; bailing loud here surfaces wiring
|
|
2970
|
+
* mistakes at mount instead of producing empty catalogs at first
|
|
2971
|
+
* keystroke.
|
|
2972
|
+
*/
|
|
2973
|
+
function useDiscovery() {
|
|
2974
|
+
const ctx = useContext(DiscoveryContext);
|
|
2975
|
+
if (!ctx) throw new Error("useDiscovery must be used inside <DiscoveryProvider>");
|
|
2976
|
+
return ctx;
|
|
2977
|
+
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Non-throwing variant — returns `null` when no provider is mounted.
|
|
2980
|
+
* Used by composable modals (`<SettingsModal>`, …) that accept catalog
|
|
2981
|
+
* props as a fallback for embedders who don't wire the full TUI
|
|
2982
|
+
* shell. The in-app flow always mounts `<DiscoveryProvider>` so the
|
|
2983
|
+
* modal sees live state; standalone embeds get the prop snapshot.
|
|
2984
|
+
*/
|
|
2985
|
+
function useDiscoveryOptional() {
|
|
2986
|
+
return useContext(DiscoveryContext);
|
|
2987
|
+
}
|
|
2988
|
+
//#endregion
|
|
2989
|
+
//#region src/chat/discovery-slot.ts
|
|
2990
|
+
function createDiscoverySlot(options) {
|
|
2991
|
+
const throttleMs = options.throttleMs ?? 3e3;
|
|
2992
|
+
const abortCtrl = new AbortController();
|
|
2993
|
+
let firstLoad = null;
|
|
2994
|
+
let refreshing = null;
|
|
2995
|
+
let lastScannedAt = 0;
|
|
2996
|
+
let aborted = false;
|
|
2997
|
+
const handleError = (err, phase) => {
|
|
2998
|
+
options.onError?.(err, phase);
|
|
2999
|
+
};
|
|
3000
|
+
const startRefresh = () => {
|
|
3001
|
+
lastScannedAt = Date.now();
|
|
3002
|
+
const run = options.walk(abortCtrl.signal).then((items) => {
|
|
3003
|
+
if (aborted) return;
|
|
3004
|
+
options.onLoad(items);
|
|
3005
|
+
}).catch((err) => {
|
|
3006
|
+
if (aborted) return;
|
|
3007
|
+
handleError(err, "refresh");
|
|
3008
|
+
}).finally(() => {
|
|
3009
|
+
if (refreshing === run) refreshing = null;
|
|
3010
|
+
});
|
|
3011
|
+
refreshing = run;
|
|
3012
|
+
return run;
|
|
3013
|
+
};
|
|
3014
|
+
const slot = {
|
|
3015
|
+
ensure() {
|
|
3016
|
+
if (aborted) return Promise.resolve([]);
|
|
3017
|
+
if (!firstLoad) {
|
|
3018
|
+
lastScannedAt = Date.now();
|
|
3019
|
+
firstLoad = options.walk(abortCtrl.signal).then((items) => {
|
|
3020
|
+
if (aborted) return [];
|
|
3021
|
+
options.onLoad(items);
|
|
3022
|
+
return items;
|
|
3023
|
+
}).catch((err) => {
|
|
3024
|
+
if (!aborted) handleError(err, "first-load");
|
|
3025
|
+
return [];
|
|
3026
|
+
});
|
|
3027
|
+
return firstLoad;
|
|
3028
|
+
}
|
|
3029
|
+
if (!refreshing && Date.now() - lastScannedAt >= throttleMs) startRefresh();
|
|
3030
|
+
return firstLoad;
|
|
3031
|
+
},
|
|
3032
|
+
refresh() {
|
|
3033
|
+
if (aborted) return Promise.resolve();
|
|
3034
|
+
if (refreshing) return refreshing;
|
|
3035
|
+
if (!firstLoad) return slot.ensure().then(() => {});
|
|
3036
|
+
return startRefresh();
|
|
3037
|
+
},
|
|
3038
|
+
isRefreshing() {
|
|
3039
|
+
return refreshing !== null;
|
|
3040
|
+
},
|
|
3041
|
+
abort() {
|
|
3042
|
+
if (aborted) return;
|
|
3043
|
+
aborted = true;
|
|
3044
|
+
abortCtrl.abort();
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
return slot;
|
|
3048
|
+
}
|
|
3049
|
+
//#endregion
|
|
2806
3050
|
//#region src/chat/themes/catppuccin.ts
|
|
2807
3051
|
const LATTE = {
|
|
2808
3052
|
rosewater: "#dc8a78",
|
|
@@ -4250,6 +4494,65 @@ function clip(text, max) {
|
|
|
4250
4494
|
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
4251
4495
|
}
|
|
4252
4496
|
//#endregion
|
|
4497
|
+
//#region src/chat/hints.ts
|
|
4498
|
+
/**
|
|
4499
|
+
* Truncate `text` to at most `max` characters, replacing the trailing
|
|
4500
|
+
* overflow with `…`. Edge cases:
|
|
4501
|
+
* - `max <= 0` → empty string (no room to render at all).
|
|
4502
|
+
* - `max === 1` → just the ellipsis glyph.
|
|
4503
|
+
* - `text.length <= max` → unchanged.
|
|
4504
|
+
*
|
|
4505
|
+
* Trailing-style truncation matches the natural read order of titles:
|
|
4506
|
+
* the prefix carries enough signal to identify the surface.
|
|
4507
|
+
*/
|
|
4508
|
+
function truncateTrailing(text, max) {
|
|
4509
|
+
if (max <= 0) return "";
|
|
4510
|
+
if (text.length <= max) return text;
|
|
4511
|
+
if (max === 1) return "…";
|
|
4512
|
+
return `${text.slice(0, max - 1)}…`;
|
|
4513
|
+
}
|
|
4514
|
+
/**
|
|
4515
|
+
* Plain-text width estimate for a list of {@link Hint}s rendered as
|
|
4516
|
+
* `<key> <label> · <key> <label> · …`. Exported so prompt-box overlays
|
|
4517
|
+
* can run the same responsive math as the bottom-bar footer when
|
|
4518
|
+
* deciding whether trigger hints fit. Pure / total.
|
|
4519
|
+
*/
|
|
4520
|
+
function hintsLength(hints) {
|
|
4521
|
+
if (hints.length === 0) return 0;
|
|
4522
|
+
return hints.reduce((sum, h, i) => sum + hintLength(h) + (i > 0 ? 3 : 0), 0);
|
|
4523
|
+
}
|
|
4524
|
+
/** Plain-text width of a single hint as rendered by `renderHintSpans`. */
|
|
4525
|
+
function hintLength(h) {
|
|
4526
|
+
return h.key.length + 1 + h.label.length + (h.extra ? h.extra.key.length + 1 + h.extra.label.length : 0);
|
|
4527
|
+
}
|
|
4528
|
+
/** Stable empty list so callers can compare by reference. */
|
|
4529
|
+
const EMPTY_HINTS = Object.freeze([]);
|
|
4530
|
+
/**
|
|
4531
|
+
* Return the longest prefix of `hints` whose rendered width fits within
|
|
4532
|
+
* `budget`. Used to degrade hint rows gracefully at narrow terminal
|
|
4533
|
+
* widths instead of letting an absolutely-positioned hint row wrap
|
|
4534
|
+
* mid-segment (which paints the overflow over surrounding borders and
|
|
4535
|
+
* looks like garbled glyphs in a TUI).
|
|
4536
|
+
*
|
|
4537
|
+
* Prefix-only (no reordering, no last-hint priority) so the survivors
|
|
4538
|
+
* keep their authored order — the user's muscle memory for "leftmost
|
|
4539
|
+
* hint = primary action" stays intact as the terminal shrinks.
|
|
4540
|
+
*/
|
|
4541
|
+
function clipHintsToWidth(hints, budget) {
|
|
4542
|
+
if (budget <= 0 || hints.length === 0) return EMPTY_HINTS;
|
|
4543
|
+
const out = [];
|
|
4544
|
+
let used = 0;
|
|
4545
|
+
for (let i = 0; i < hints.length; i++) {
|
|
4546
|
+
const h = hints[i];
|
|
4547
|
+
const cost = (i > 0 ? 3 : 0) + hintLength(h);
|
|
4548
|
+
if (used + cost > budget) break;
|
|
4549
|
+
out.push(h);
|
|
4550
|
+
used += cost;
|
|
4551
|
+
}
|
|
4552
|
+
if (out.length === 0) return EMPTY_HINTS;
|
|
4553
|
+
return out.length === hints.length ? hints : out;
|
|
4554
|
+
}
|
|
4555
|
+
//#endregion
|
|
4253
4556
|
//#region src/chat/interactions.tsx
|
|
4254
4557
|
const PRESENT_PLAN_TOOL = "present_plan";
|
|
4255
4558
|
const ASK_USER_TOOL = "ask_user";
|
|
@@ -4717,6 +5020,79 @@ function makeRequestInteraction(actions) {
|
|
|
4717
5020
|
});
|
|
4718
5021
|
}
|
|
4719
5022
|
//#endregion
|
|
5023
|
+
//#region src/chat/markdown-segments.ts
|
|
5024
|
+
/**
|
|
5025
|
+
* Split markdown text into alternating prose / fenced-code segments.
|
|
5026
|
+
*
|
|
5027
|
+
* Recognizes both ` ``` ` and `~~~` fences, with arbitrary length ≥ 3,
|
|
5028
|
+
* matching CommonMark's "fence indicator must be at least as long to
|
|
5029
|
+
* close" rule. Info-strings (the bit after the opening fence) are kept
|
|
5030
|
+
* as the `lang` hint; any trailing whitespace is trimmed. Closing
|
|
5031
|
+
* fences are detected on a line of their own (whitespace tolerated).
|
|
5032
|
+
*
|
|
5033
|
+
* Unclosed fences fall back to emitting the would-be-code body as a
|
|
5034
|
+
* trailing prose segment so no content is dropped — finalized markdown
|
|
5035
|
+
* isn't expected to ship one, but the fallback keeps the renderer
|
|
5036
|
+
* truthful when the model produces malformed output.
|
|
5037
|
+
*
|
|
5038
|
+
* Exported for unit tests.
|
|
5039
|
+
*/
|
|
5040
|
+
function splitMarkdownCodeBlocks(text) {
|
|
5041
|
+
const lines = text.split("\n");
|
|
5042
|
+
const segments = [];
|
|
5043
|
+
let prose = [];
|
|
5044
|
+
let code = [];
|
|
5045
|
+
let inFence = false;
|
|
5046
|
+
let fenceChar = "";
|
|
5047
|
+
let fenceLen = 0;
|
|
5048
|
+
let lang = "";
|
|
5049
|
+
const flushProse = () => {
|
|
5050
|
+
if (prose.length > 0 && prose[prose.length - 1] === "") prose.pop();
|
|
5051
|
+
if (prose.length > 0) {
|
|
5052
|
+
segments.push({
|
|
5053
|
+
kind: "prose",
|
|
5054
|
+
content: prose.join("\n")
|
|
5055
|
+
});
|
|
5056
|
+
prose = [];
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
let trimLeadingProseBlank = false;
|
|
5060
|
+
for (const line of lines) if (!inFence) {
|
|
5061
|
+
const open = line.match(/^(`{3,}|~{3,})([^\n`~]*)$/);
|
|
5062
|
+
if (open) {
|
|
5063
|
+
flushProse();
|
|
5064
|
+
inFence = true;
|
|
5065
|
+
fenceChar = open[1][0];
|
|
5066
|
+
fenceLen = open[1].length;
|
|
5067
|
+
lang = open[2].trim();
|
|
5068
|
+
code = [];
|
|
5069
|
+
continue;
|
|
5070
|
+
}
|
|
5071
|
+
if (trimLeadingProseBlank) {
|
|
5072
|
+
trimLeadingProseBlank = false;
|
|
5073
|
+
if (line === "" && prose.length === 0) continue;
|
|
5074
|
+
}
|
|
5075
|
+
prose.push(line);
|
|
5076
|
+
} else {
|
|
5077
|
+
const close = line.match(/^(`{3,}|~{3,})\s*$/);
|
|
5078
|
+
if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
|
|
5079
|
+
segments.push({
|
|
5080
|
+
kind: "code",
|
|
5081
|
+
content: code.join("\n"),
|
|
5082
|
+
lang
|
|
5083
|
+
});
|
|
5084
|
+
inFence = false;
|
|
5085
|
+
code = [];
|
|
5086
|
+
trimLeadingProseBlank = true;
|
|
5087
|
+
continue;
|
|
5088
|
+
}
|
|
5089
|
+
code.push(line);
|
|
5090
|
+
}
|
|
5091
|
+
if (inFence) prose.push(`${fenceChar.repeat(fenceLen)}${lang}`, ...code);
|
|
5092
|
+
flushProse();
|
|
5093
|
+
return segments;
|
|
5094
|
+
}
|
|
5095
|
+
//#endregion
|
|
4720
5096
|
//#region src/chat/mcp-auth-state.ts
|
|
4721
5097
|
/**
|
|
4722
5098
|
* Apply one event to the state map. Pure, immutable — returns a new map
|
|
@@ -6040,6 +6416,19 @@ function useStreamBuffer(setEvents, options) {
|
|
|
6040
6416
|
const tickerRef = useRef(null);
|
|
6041
6417
|
const getSmoothRef = useRef(options?.getSmooth);
|
|
6042
6418
|
getSmoothRef.current = options?.getSmooth;
|
|
6419
|
+
/**
|
|
6420
|
+
* Updaters queued while smooth-streaming was still draining backlog —
|
|
6421
|
+
* tool-call appends, finalize-markdown transforms, error events.
|
|
6422
|
+
* Applied in FIFO order at the END of `tick()` once every bucket is
|
|
6423
|
+
* empty, so the trailing typewriter characters land BEFORE the
|
|
6424
|
+
* follow-up event instead of being clobbered by an immediate drain.
|
|
6425
|
+
*
|
|
6426
|
+
* The buffer treats markdown deltas as the authoritative ordering
|
|
6427
|
+
* source: a tool call queued behind 100 buffered chars surfaces after
|
|
6428
|
+
* those 100 chars have typed out, preserving the visual stream order
|
|
6429
|
+
* the user is reading.
|
|
6430
|
+
*/
|
|
6431
|
+
const pendingUpdatersRef = useRef([]);
|
|
6043
6432
|
const stopTicker = useCallback(() => {
|
|
6044
6433
|
if (tickerRef.current) {
|
|
6045
6434
|
clearInterval(tickerRef.current);
|
|
@@ -6047,27 +6436,45 @@ function useStreamBuffer(setEvents, options) {
|
|
|
6047
6436
|
}
|
|
6048
6437
|
}, []);
|
|
6049
6438
|
/**
|
|
6050
|
-
*
|
|
6051
|
-
*
|
|
6052
|
-
*
|
|
6053
|
-
*
|
|
6054
|
-
*
|
|
6439
|
+
* Has at least one bucket got unflushed *markdown* content?
|
|
6440
|
+
*
|
|
6441
|
+
* Thinking content is excluded: it always flushes in full on every
|
|
6442
|
+
* tick (see `tick()`), so its presence in a bucket only matters until
|
|
6443
|
+
* the next 16 ms cycle — never long enough to justify deferring an
|
|
6444
|
+
* end-of-turn updater.
|
|
6055
6445
|
*/
|
|
6056
|
-
const
|
|
6446
|
+
const hasMarkdownBacklog = useCallback(() => {
|
|
6447
|
+
for (const bucket of bucketsRef.current.values()) if (bucket.markdown.length > 0) return true;
|
|
6448
|
+
return false;
|
|
6449
|
+
}, []);
|
|
6450
|
+
/**
|
|
6451
|
+
* Drain every bucket in full and synchronously run any queued
|
|
6452
|
+
* post-drain updaters plus the caller-provided one. Used by the
|
|
6453
|
+
* fast paths below — `reset`, batched-mode `appendImmediate` /
|
|
6454
|
+
* `flushAndUpdate`, and smooth-mode calls where no markdown
|
|
6455
|
+
* backlog remains.
|
|
6456
|
+
*/
|
|
6457
|
+
const drainNow = useCallback((updater) => {
|
|
6057
6458
|
stopTicker();
|
|
6058
6459
|
const buckets = Array.from(bucketsRef.current.values());
|
|
6059
6460
|
bucketsRef.current.clear();
|
|
6060
|
-
|
|
6461
|
+
const queued = pendingUpdatersRef.current;
|
|
6462
|
+
pendingUpdatersRef.current = [];
|
|
6463
|
+
if (!buckets.some((b) => b.markdown.length > 0 || b.thinking.length > 0) && !updater && queued.length === 0) return;
|
|
6061
6464
|
setEvents((prev) => {
|
|
6062
6465
|
let merged = prev;
|
|
6063
6466
|
for (const bucket of buckets) merged = applyBucket(merged, bucket);
|
|
6064
|
-
|
|
6467
|
+
for (const u of queued) merged = u(merged);
|
|
6468
|
+
if (updater) merged = updater(merged);
|
|
6469
|
+
return merged;
|
|
6065
6470
|
});
|
|
6066
6471
|
}, [setEvents, stopTicker]);
|
|
6067
6472
|
/**
|
|
6068
6473
|
* One tick of the continuous drain loop. Walks every live bucket and
|
|
6069
|
-
* commits a portion of its content based on the current mode.
|
|
6070
|
-
*
|
|
6474
|
+
* commits a portion of its content based on the current mode. Once
|
|
6475
|
+
* every bucket empties out, fires any post-drain updaters that were
|
|
6476
|
+
* queued while smooth streaming was still in progress (turn
|
|
6477
|
+
* finalize, tool calls, errors) and stops the ticker.
|
|
6071
6478
|
*
|
|
6072
6479
|
* `thinking` content always flushes immediately even in smooth mode —
|
|
6073
6480
|
* it's an internal-reasoning surface, surfaced for transparency rather
|
|
@@ -6100,9 +6507,11 @@ function useStreamBuffer(setEvents, options) {
|
|
|
6100
6507
|
});
|
|
6101
6508
|
if (bucket.markdown.length > 0) stillHasContent = true;
|
|
6102
6509
|
}
|
|
6103
|
-
|
|
6510
|
+
const queued = !stillHasContent && pendingUpdatersRef.current.length > 0 ? pendingUpdatersRef.current.splice(0, pendingUpdatersRef.current.length) : [];
|
|
6511
|
+
if (portions.length > 0 || queued.length > 0) setEvents((prev) => {
|
|
6104
6512
|
let next = prev;
|
|
6105
6513
|
for (const portion of portions) next = applyBucket(next, portion);
|
|
6514
|
+
for (const u of queued) next = u(next);
|
|
6106
6515
|
return next;
|
|
6107
6516
|
});
|
|
6108
6517
|
if (!stillHasContent) stopTicker();
|
|
@@ -6111,9 +6520,40 @@ function useStreamBuffer(setEvents, options) {
|
|
|
6111
6520
|
if (tickerRef.current) return;
|
|
6112
6521
|
tickerRef.current = setInterval(tick, TICK_INTERVAL_MS);
|
|
6113
6522
|
}, [tick]);
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6523
|
+
/**
|
|
6524
|
+
* Decide between fast-drain and deferred-drain for an
|
|
6525
|
+
* updater-bearing call (`appendImmediate`, `flushAndUpdate`).
|
|
6526
|
+
*
|
|
6527
|
+
* Smooth mode + markdown backlog → ENQUEUE the updater behind the
|
|
6528
|
+
* trailing characters; the ticker picks it up once buckets empty.
|
|
6529
|
+
* Anything else → fall through to `drainNow` so the call retains
|
|
6530
|
+
* its synchronous semantics.
|
|
6531
|
+
*/
|
|
6532
|
+
const drainOrDefer = useCallback((updater) => {
|
|
6533
|
+
if ((getSmoothRef.current?.() ?? true) && hasMarkdownBacklog()) {
|
|
6534
|
+
pendingUpdatersRef.current.push(updater);
|
|
6535
|
+
ensureTicker();
|
|
6536
|
+
return;
|
|
6537
|
+
}
|
|
6538
|
+
drainNow(updater);
|
|
6539
|
+
}, [
|
|
6540
|
+
drainNow,
|
|
6541
|
+
ensureTicker,
|
|
6542
|
+
hasMarkdownBacklog
|
|
6543
|
+
]);
|
|
6544
|
+
const flush = useCallback(() => {
|
|
6545
|
+
if ((getSmoothRef.current?.() ?? true) && hasMarkdownBacklog()) {
|
|
6546
|
+
ensureTicker();
|
|
6547
|
+
return;
|
|
6548
|
+
}
|
|
6549
|
+
drainNow();
|
|
6550
|
+
}, [
|
|
6551
|
+
drainNow,
|
|
6552
|
+
ensureTicker,
|
|
6553
|
+
hasMarkdownBacklog
|
|
6554
|
+
]);
|
|
6555
|
+
const flushAndUpdate = useCallback((update) => drainOrDefer(update), [drainOrDefer]);
|
|
6556
|
+
const appendImmediate = useCallback((evt) => drainOrDefer((events) => [...events, evt]), [drainOrDefer]);
|
|
6117
6557
|
const queueStreamDelta = useCallback((kind, delta, source) => {
|
|
6118
6558
|
if (!delta) return;
|
|
6119
6559
|
const owner = source?.childId ?? PARENT_OWNER;
|
|
@@ -6130,6 +6570,7 @@ function useStreamBuffer(setEvents, options) {
|
|
|
6130
6570
|
const reset = useCallback(() => {
|
|
6131
6571
|
stopTicker();
|
|
6132
6572
|
bucketsRef.current.clear();
|
|
6573
|
+
pendingUpdatersRef.current = [];
|
|
6133
6574
|
}, [stopTicker]);
|
|
6134
6575
|
return useMemo(() => ({
|
|
6135
6576
|
queueStreamDelta,
|
|
@@ -6422,6 +6863,46 @@ function formatBytes(bytes) {
|
|
|
6422
6863
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
6423
6864
|
}
|
|
6424
6865
|
//#endregion
|
|
6866
|
+
//#region src/chat/transcript-anchors.ts
|
|
6867
|
+
/**
|
|
6868
|
+
* Per-item anchor ids for auto-scroll. Walks `items` in render order
|
|
6869
|
+
* and, for each event, returns either:
|
|
6870
|
+
* - `'turn-anchor-<turnId>'` — the first event of this turn (the
|
|
6871
|
+
* scroll target).
|
|
6872
|
+
* - `undefined` — later event of an already-tagged turn (or a
|
|
6873
|
+
* synthetic event with no `turnId`).
|
|
6874
|
+
*
|
|
6875
|
+
* `ids[i]` is a tuple per item: length 1 for plain events, length N
|
|
6876
|
+
* for subagent runs (one entry per inner event). `idByTurn` is the
|
|
6877
|
+
* inverse lookup used by the scroll effect. `lastTurnId` is the
|
|
6878
|
+
* most-recently-rendered turn — the scroll effect special-cases it to
|
|
6879
|
+
* snap to bottom rather than scroll the anchor into view (which would
|
|
6880
|
+
* stop short of the actual tail).
|
|
6881
|
+
*
|
|
6882
|
+
* Exported so the anchor-tagging matrix can be unit-tested without
|
|
6883
|
+
* rendering anything.
|
|
6884
|
+
*/
|
|
6885
|
+
function computeTurnAnchors(items) {
|
|
6886
|
+
const idByTurn = /* @__PURE__ */ new Map();
|
|
6887
|
+
let lastTurnId;
|
|
6888
|
+
const tag = (turnId) => {
|
|
6889
|
+
if (!turnId) return void 0;
|
|
6890
|
+
lastTurnId = turnId;
|
|
6891
|
+
if (idByTurn.has(turnId)) return void 0;
|
|
6892
|
+
const id = `turn-anchor-${turnId}`;
|
|
6893
|
+
idByTurn.set(turnId, id);
|
|
6894
|
+
return id;
|
|
6895
|
+
};
|
|
6896
|
+
const ids = [];
|
|
6897
|
+
for (const item of items) if (item.kind === "event") ids.push([tag(item.event.turnId)]);
|
|
6898
|
+
else ids.push(item.events.map((e) => tag(e.turnId)));
|
|
6899
|
+
return {
|
|
6900
|
+
idByTurn,
|
|
6901
|
+
ids,
|
|
6902
|
+
lastTurnId
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
//#endregion
|
|
6425
6906
|
//#region src/chat/turn-operations.ts
|
|
6426
6907
|
/**
|
|
6427
6908
|
* Fork — keep every turn up to and including `turnId`, then strip any
|
|
@@ -6537,6 +7018,6 @@ function countNeighbors(turnIds, turnId) {
|
|
|
6537
7018
|
};
|
|
6538
7019
|
}
|
|
6539
7020
|
//#endregion
|
|
6540
|
-
export {
|
|
7021
|
+
export { useMcpAuthState as $, setProviderCredential as $n, lastContextSizeFromTurns as $t, getSafelist as A, SKILLS_TRIGGER as An, SUBAGENT_GUIDANCE as Ar, useSettings as At, buildModelCatalog as B, useCompletion as Bn, createDiscoverySlot as Bt, resolveSessionExportTarget as C, ensureKeybindingsFile as Cn, COMMUNICATION_DOCTRINE as Cr, listProjectFiles as Ct, useSafeModeQueue as D, parseBindingSpec as Dn, INTERACTION_GUIDANCE_NO_PROMPTS as Dr, SETTINGS_TOGGLES as Dt, useSafeModeActions as E, mergeKeybindings as En, INTERACTION_GUIDANCE as Er, SETTINGS_CHOICES as Et, suggestSafelistEntry as F, uniqueFilesFromReferences as Fn, VAPORWAVE_THEME as Ft, discoverProjectMcps as G, bootTick as Gn, useConfig as Gt, indexOfEntry as H, buildLinearRamp as Hn, useDiscovery as Ht, writeProjects as I, applyInsert as In, CATPPUCCIN_FRAPPE as It, createFileMcpCredentialStore as J, applyApiKeyEnv as Jn, deriveSessionTitle as Jt, parseMcpsFile as K, shouldAutoCompact as Kn, resolveConfig as Kt, splitPromptSegments as L, collectReferences as Ln, CATPPUCCIN_LATTE as Lt, matchesSafelistEntry as M, uniqueSkillNamesFromReferences as Mn, buildBuildSystem as Mr, DEFAULT_THEME as Mt, projectsFilePath as N, FILES_TRIGGER as Nn, buildPlanSystem as Nr, resolveChipColor as Nt, IMPLICITLY_SAFE_TOOLS as O, readKeybindings as On, PLAN_MODE_DOCTRINE as Or, SettingsProvider as Ot, readProjects as P, createFilesCompletionProvider as Pn, envSection as Pr, resolveTheme as Pt, useMcpAuthDispatch as Q, removeProviderCredential as Qn, isVisible as Qt, runOAuthLogin as R, findActiveTrigger as Rn, CATPPUCCIN_MACCHIATO as Rt, renderSession as S, KEYBINDING_DEF_BY_ACTION as Sn, ACTIONS_WITH_CARE_DOCTRINE as Sr, shortId as St, SafeModeProvider as T, matchesBinding as Tn, IDENTITY_PREFIX as Tr, DEFAULT_SETTINGS as Tt, buildMcpServers as U, tryOpenBrowser as Un, useDiscoveryOptional as Ut, filterModelCatalog as V, blendHsl as Vn, DiscoveryProvider as Vt, defaultMcpsConfigPaths as W, bootProfileEnabled as Wn, ConfigProvider as Wt, patchMcpCredential as X, readCredentials as Xn, isEditErrorResult as Xt, mcpCredentialsPath as Y, credentialsPath as Yn, eventsFromTurns as Yt, McpAuthProvider as Z, readProviderCredential as Zn, isTurnHighlighted as Zt, turnContextSize as _, splitLines as _n, DEFAULT_PERSIST_EXCLUDE_TOOLS as _r, cleanTitle as _t, computeTurnAnchors as a, stripSpawnTokensLine as an, credKeyOf as ar, PRESENT_PLAN_TOOL as at, defaultSkillScanPaths as b, DEFAULT_KEYBINDINGS as bn, resolveAgentId as br, compactPath as bt, formatToolCall as c, toolCallPreview as cn, getModelInfo as cr, isInteractionTool as ct, useSelectStyle as d, buildContextualDiff as dn, openaiDescriptor as dr, serializeInteractionResponse as dt, listSessionMeta as en, writeCredentials as er, getMcpAuthStatus as et, useSurfaces as f, buildUnifiedDiff as fn, openrouterDescriptor as fr, useInteractionsActions as ft, finalizeStreamingMarkdownForOwner as g, filetypeFromPath as gn, DEFAULT_AGENT_ID as gr, truncateTrailing as gt, finalizeStreamingMarkdown as h, extractEditPayload as hn, BUILTIN_AGENTS as hr, hintsLength as ht, turnAsText as i, selectableTurnIds as in, cerebrasDescriptor as ir, InteractionsProvider as it, isOnSafelist as j, createSkillsCompletionProvider as jn, TOKEN_DISCIPLINE_DOCTRINE as jr, BUILTIN_THEMES as jt, addToSafelist as k, stripJsonComments as kn, PLAN_MODE_DOCTRINE_NO_PROMPTS as kr, clampFps as kt, ThemeProvider as l, toolResultText as ln, modelSupportsReasoning as lr, makeRequestInteraction as lt, useTheme as m, computeLineDiff as mn, BUILD_AGENT as mr, clipHintsToWidth as mt, deleteTurnSafely as n, marginTopFor as nn, OUTPUT_RESERVE_TOKENS as nr, splitMarkdownCodeBlocks as nt, TOOL_DISPLAY as o, sumRunCosts as on, effectiveContextWindow as or, buildResumedToolResultsTurn as ot, useSyntaxStyles as p, computeInlineDiff as pn, piIdOf as pr, useInteractionsQueue as pt, projectUserPaths as q, detectAuth as qn, createStateStore as qt, truncateTurnsAt as r, saveState as rn, anthropicDescriptor as rr, ASK_USER_TOOL as rt, displayNameFor as s, titleFromTurns as sn, getContextWindow as sr, createInteractionTools as st, countNeighbors as t, loadState as tn, BUILTIN_PROVIDERS as tr, reduceMcpAuth as tt, useColors as u, turnSelectionOwnership as un, modelsForDescriptor as ur, pendingInteractionsFromTurns as ut, useStreamBuffer as v, tokenize as vn, PLAN_AGENT as vr, generateSessionTitle as vt, writeSessionExport as w, keybindingsPath as wn, DOING_TASKS_DOCTRINE as wr, useEnabledToggleSet as wt, discoverProjectSkills as x, KEYBINDING_DEFS as xn, singleAgentRegistry as xr, fmtTokens as xt, buildSkillsConfig as y, findGitRoot$1 as yn, accentColor as yr, ageString as yt, supportsOAuth as z, mergeReferences as zn, CATPPUCCIN_MOCHA as zt };
|
|
6541
7022
|
|
|
6542
|
-
//# sourceMappingURL=turn-operations-
|
|
7023
|
+
//# sourceMappingURL=turn-operations-CeLlc7jt.js.map
|