dskcode 0.1.39 → 0.1.41

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/index.js CHANGED
@@ -805,9 +805,9 @@ var LOGO_LINES = [
805
805
  ];
806
806
 
807
807
  // src/ui/ChatSession.tsx
808
- import { Box as Box11, Text as Text12, useInput, Static } from "ink";
808
+ import { Box as Box12, Text as Text13, useInput, Static } from "ink";
809
809
  import TextInput from "ink-text-input";
810
- import { useEffect as useEffect5, useState as useState5, useCallback as useCallback2, useMemo, useRef as useRef4 } from "react";
810
+ import { useEffect as useEffect5, useState as useState5, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef4 } from "react";
811
811
 
812
812
  // src/ui/useDoubleCtrlC.ts
813
813
  import { useState as useState2, useCallback, useEffect as useEffect2, useRef } from "react";
@@ -838,7 +838,7 @@ function useDoubleCtrlC(onExit) {
838
838
  }
839
839
 
840
840
  // src/ui/ChatSession.tsx
841
- import InkSpinner3 from "ink-spinner";
841
+ import InkSpinner4 from "ink-spinner";
842
842
 
843
843
  // src/ui/AssistantMessage.tsx
844
844
  import { Box as Box5, Text as Text6 } from "ink";
@@ -1160,23 +1160,207 @@ function formatMoney(yuan) {
1160
1160
 
1161
1161
  // src/ui/ToolCallBlock.tsx
1162
1162
  import { Box as Box3, Text as Text4 } from "ink";
1163
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1164
- function formatArgsSummary(args) {
1163
+
1164
+ // src/agent/tool-call-summary.ts
1165
+ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
1166
+ "password",
1167
+ "token",
1168
+ "apikey",
1169
+ "api_key",
1170
+ "secret",
1171
+ "authorization",
1172
+ "auth",
1173
+ "credential",
1174
+ "credentials",
1175
+ "private_key",
1176
+ "privatekey"
1177
+ ]);
1178
+ function templateToolCall(toolName, args) {
1179
+ if (!isPlainObject(args)) {
1180
+ return `${toolName}()`;
1181
+ }
1182
+ switch (toolName) {
1183
+ case "read_file":
1184
+ return `${toolName}(path=${shortStr(args.path)})`;
1185
+ case "write_file":
1186
+ return `${toolName}(path=${shortStr(args.path)})`;
1187
+ case "edit_file":
1188
+ return `${toolName}(path=${shortStr(args.path)}, old_text=${shortStr(args.old_text, 30)})`;
1189
+ case "multi_edit": {
1190
+ const edits = Array.isArray(args.edits) ? args.edits.length : 0;
1191
+ return `${toolName}(path=${shortStr(args.path)}, edits=${edits})`;
1192
+ }
1193
+ case "delete_range": {
1194
+ return `${toolName}(path=${shortStr(args.path)})`;
1195
+ }
1196
+ case "bash":
1197
+ return `${toolName}(command=${shortStr(args.command, 60)})`;
1198
+ case "ls":
1199
+ return `${toolName}(path=${shortStr(args.path ?? ".")})`;
1200
+ case "glob":
1201
+ return `${toolName}(pattern=${shortStr(args.pattern)})`;
1202
+ case "grep":
1203
+ return `${toolName}(pattern=${shortStr(args.pattern)}, path=${shortStr(args.path ?? ".")})`;
1204
+ case "fetch":
1205
+ return `${toolName}(url=${shortStr(args.url, 60)})`;
1206
+ default:
1207
+ const keys = Object.keys(args);
1208
+ if (keys.length === 0) return `${toolName}()`;
1209
+ const preview = keys.slice(0, 4).map((k) => `${k}=\u2026`).join(", ");
1210
+ const more = keys.length > 4 ? `, +${keys.length - 4}` : "";
1211
+ return `${toolName}(${preview}${more})`;
1212
+ }
1213
+ }
1214
+ function maskSensitive(args) {
1215
+ return maskRecursive(args, /* @__PURE__ */ new WeakSet());
1216
+ }
1217
+ function maskRecursive(value, seen) {
1218
+ if (value === null || value === void 0) return value;
1219
+ if (typeof value !== "object") return value;
1220
+ const obj = value;
1221
+ if (seen.has(obj)) return "****";
1222
+ seen.add(obj);
1223
+ if (Array.isArray(value)) {
1224
+ return value.map((v) => maskRecursive(v, seen));
1225
+ }
1226
+ const out = {};
1227
+ for (const [k, v] of Object.entries(obj)) {
1228
+ if (SENSITIVE_KEYS.has(k.toLowerCase())) {
1229
+ out[k] = "****";
1230
+ } else {
1231
+ out[k] = maskRecursive(v, seen);
1232
+ }
1233
+ }
1234
+ return out;
1235
+ }
1236
+ function formatArgsForDisplay(args, opts = {}) {
1237
+ const valueMaxLen = opts.valueMaxLen ?? 80;
1238
+ const doMask = opts.maskSensitive !== false;
1239
+ const maxKeys = opts.maxKeys ?? 6;
1240
+ if (!isPlainObject(args)) {
1241
+ const s = formatValue(args, valueMaxLen);
1242
+ return s;
1243
+ }
1244
+ const masked = doMask ? maskSensitive(args) : args;
1245
+ const entries = Object.entries(masked);
1246
+ const shown = entries.slice(0, maxKeys);
1247
+ const lines = shown.map(([k, v]) => `${k}: ${formatValue(v, valueMaxLen)}`);
1248
+ if (entries.length > maxKeys) {
1249
+ lines.push(`+${entries.length - maxKeys} more`);
1250
+ }
1251
+ return lines.join("\n");
1252
+ }
1253
+ function formatValue(v, maxLen = 80) {
1254
+ if (v === null) return "null";
1255
+ if (v === void 0) return "undefined";
1256
+ if (typeof v === "string") {
1257
+ return v.length > maxLen ? v.slice(0, maxLen - 1) + "\u2026" : v;
1258
+ }
1259
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
1260
+ if (Array.isArray(v)) {
1261
+ if (v.length <= 3) return JSON.stringify(v);
1262
+ return `[\u2026${v.length} items\u2026]`;
1263
+ }
1264
+ if (typeof v === "object") {
1265
+ const obj = v;
1266
+ const keys = Object.keys(obj);
1267
+ if (keys.length <= 3) return JSON.stringify(obj);
1268
+ return `{\u2026${keys.length} keys\u2026}`;
1269
+ }
1270
+ return JSON.stringify(v);
1271
+ }
1272
+ function shortStr(v, maxLen = 40) {
1273
+ if (v === void 0 || v === null) return String(v);
1274
+ if (typeof v !== "string") return formatValue(v, maxLen);
1275
+ if (v.length === 0) return "(\u7A7A)";
1276
+ if (v.length > maxLen) return v.slice(0, maxLen - 1) + "\u2026";
1277
+ return v;
1278
+ }
1279
+ function isPlainObject(v) {
1280
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1281
+ }
1282
+
1283
+ // src/agent/tool-call-parser.ts
1284
+ function parseToolCallArgs(raw) {
1285
+ if (raw === "" || raw === void 0 || raw === null) {
1286
+ return { ok: true, args: {}, fixed: "" };
1287
+ }
1165
1288
  try {
1166
- const parsed = JSON.parse(args);
1167
- const lines = Object.entries(parsed).map(([key, value]) => {
1168
- const val = String(value);
1169
- const truncated = val.length > 80 ? val.slice(0, 77) + "..." : val;
1170
- return ` ${key}: ${truncated}`;
1171
- });
1172
- return lines.join("\n");
1173
- } catch {
1174
- const truncated = args.length > 120 ? args.slice(0, 117) + "..." : args;
1175
- return ` ${truncated}`;
1289
+ const args = JSON.parse(raw);
1290
+ return { ok: true, args, fixed: raw };
1291
+ } catch (firstErr) {
1292
+ const fixed = fixStreamedJson(raw);
1293
+ try {
1294
+ const args = JSON.parse(fixed);
1295
+ return { ok: true, args, fixed };
1296
+ } catch (secondErr) {
1297
+ const firstMsg = firstErr instanceof Error ? firstErr.message : String(firstErr);
1298
+ if (isLikelyPartialJson(firstMsg, raw)) {
1299
+ return {
1300
+ ok: false,
1301
+ reason: "PARTIAL",
1302
+ partial: raw,
1303
+ hint: "\u53C2\u6570 JSON \u5C1A\u672A\u6536\u5168\uFF0C\u6D41\u5F0F\u62FC\u63A5\u4E2D\uFF1B\u53EF\u80FD\u662F\u7F51\u7EDC/\u5FC3\u8DF3\u5BFC\u81F4\u534A\u622A"
1304
+ };
1305
+ }
1306
+ return {
1307
+ ok: false,
1308
+ reason: "INVALID_JSON",
1309
+ raw,
1310
+ error: firstMsg
1311
+ };
1312
+ }
1313
+ }
1314
+ }
1315
+ function fixStreamedJson(partial) {
1316
+ let end = partial.length;
1317
+ let backslashes = 0;
1318
+ for (let i = partial.length - 1; i >= 0; i--) {
1319
+ if (partial[i] === "\\") backslashes++;
1320
+ else break;
1321
+ }
1322
+ if (backslashes % 2 === 1) end--;
1323
+ let json = partial.slice(0, end);
1324
+ const stack = [];
1325
+ let inString = false;
1326
+ let escaped = false;
1327
+ for (let i = 0; i < json.length; i++) {
1328
+ const ch = json[i];
1329
+ if (escaped) {
1330
+ escaped = false;
1331
+ continue;
1332
+ }
1333
+ if (ch === "\\") {
1334
+ escaped = true;
1335
+ continue;
1336
+ }
1337
+ if (ch === '"') {
1338
+ inString = !inString;
1339
+ continue;
1340
+ }
1341
+ if (inString) continue;
1342
+ if (ch === "{") stack.push("}");
1343
+ else if (ch === "[") stack.push("]");
1344
+ else if (ch === "}" || ch === "]") stack.pop();
1176
1345
  }
1346
+ if (inString) json += '"';
1347
+ while (stack.length > 0) json += stack.pop();
1348
+ return json;
1177
1349
  }
1178
- function ToolCallBlock({ call }) {
1179
- const argsDisplay = formatArgsSummary(call.arguments);
1350
+ function isLikelyPartialJson(errMsg, raw) {
1351
+ if (errMsg.includes("Unexpected end of JSON")) return true;
1352
+ if (errMsg.includes("Unterminated string")) return true;
1353
+ if (raw.length < 5) return true;
1354
+ return false;
1355
+ }
1356
+
1357
+ // src/ui/ToolCallBlock.tsx
1358
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1359
+ function ToolCallBlock({ call, parsedArgs, schemaIssues }) {
1360
+ const resolvedArgs = parsedArgs ?? resolveArgsFromRaw(call.arguments);
1361
+ const safeArgs = maskSensitive(resolvedArgs);
1362
+ const summary = templateToolCall(call.name, safeArgs);
1363
+ const argsDisplay = formatArgsForDisplay(safeArgs);
1180
1364
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
1181
1365
  /* @__PURE__ */ jsxs3(Box3, { children: [
1182
1366
  /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
@@ -1188,9 +1372,34 @@ function ToolCallBlock({ call }) {
1188
1372
  "\u2500".repeat(Math.max(1, 30 - call.name.length))
1189
1373
  ] })
1190
1374
  ] }),
1191
- /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: argsDisplay }) })
1375
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 2, children: [
1376
+ /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: summary }),
1377
+ /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: argsDisplay })
1378
+ ] }),
1379
+ schemaIssues && schemaIssues.length > 0 && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 2, children: [
1380
+ /* @__PURE__ */ jsxs3(Text4, { color: "yellow", children: [
1381
+ "\u26A0 schema \u95EE\u9898\uFF08",
1382
+ schemaIssues.length,
1383
+ "\uFF09"
1384
+ ] }),
1385
+ schemaIssues.slice(0, 3).map((issue, i) => /* @__PURE__ */ jsxs3(Text4, { color: "yellow", children: [
1386
+ " - ",
1387
+ issue.message
1388
+ ] }, i)),
1389
+ schemaIssues.length > 3 && /* @__PURE__ */ jsxs3(Text4, { color: "yellow", children: [
1390
+ " ...\u8FD8\u6709 ",
1391
+ schemaIssues.length - 3,
1392
+ " \u6761"
1393
+ ] })
1394
+ ] })
1192
1395
  ] });
1193
1396
  }
1397
+ function resolveArgsFromRaw(raw) {
1398
+ if (!raw) return {};
1399
+ const parsed = parseToolCallArgs(raw);
1400
+ if (parsed.ok) return parsed.args;
1401
+ return {};
1402
+ }
1194
1403
 
1195
1404
  // src/ui/HighlightedText.tsx
1196
1405
  import { Box as Box4, Text as Text5 } from "ink";
@@ -1981,10 +2190,214 @@ function TodoListPanel({ items }) {
1981
2190
  ] });
1982
2191
  }
1983
2192
 
2193
+ // src/ui/CompactionProgress.tsx
2194
+ import { Box as Box10, Text as Text11 } from "ink";
2195
+ import InkSpinner3 from "ink-spinner";
2196
+ import { useMemo } from "react";
2197
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2198
+ function estimateProgress(state) {
2199
+ if (state.phase === "idle") return 0;
2200
+ if (state.phase === "done") return 100;
2201
+ if (state.phase === "error") return 100;
2202
+ const p = state.progress;
2203
+ if (!p) return 5;
2204
+ if (p.type === "start") {
2205
+ return 10;
2206
+ }
2207
+ if (p.type === "summary_delta") {
2208
+ const ratio = Math.min(p.totalSoFar.length / 800, 1);
2209
+ return Math.min(10 + ratio * 80, 90);
2210
+ }
2211
+ if (p.type === "fallback") {
2212
+ return 95;
2213
+ }
2214
+ return 5;
2215
+ }
2216
+ function phaseLabel(state) {
2217
+ if (state.phase === "error") {
2218
+ return { icon: "\u2717", text: "\u538B\u7F29\u51FA\u9519", color: "#ff6347" };
2219
+ }
2220
+ if (state.phase === "done") {
2221
+ if (state.strategy === "fallback") {
2222
+ return { icon: "\u2714", text: "\u538B\u7F29\u5B8C\u6210\uFF08\u672C\u5730\u515C\u5E95\uFF09", color: "#ff9800" };
2223
+ }
2224
+ return { icon: "\u2714", text: "\u538B\u7F29\u5B8C\u6210", color: "#00ff41" };
2225
+ }
2226
+ const p = state.progress;
2227
+ if (!p) {
2228
+ return { icon: "\u280B", text: "\u51C6\u5907\u538B\u7F29...", color: "#00ffff" };
2229
+ }
2230
+ if (p.type === "start") {
2231
+ return { icon: "\u280B", text: `\u51C6\u5907\u6458\u8981 ${p.droppedTurns} \u4E2A\u65E7\u56DE\u5408...`, color: "#00ffff" };
2232
+ }
2233
+ if (p.type === "summary_delta") {
2234
+ return { icon: "\u280B", text: "\u8C03\u7528 LLM \u751F\u6210\u6458\u8981...", color: "#00ffff" };
2235
+ }
2236
+ if (p.type === "fallback") {
2237
+ return { icon: "\u26A0", text: `LLM \u6458\u8981\u5931\u8D25\uFF0C\u4F7F\u7528\u672C\u5730\u515C\u5E95\uFF1A${p.reason}`, color: "#ff9800" };
2238
+ }
2239
+ return { icon: "\u280B", text: "\u538B\u7F29\u4E2D...", color: "#00ffff" };
2240
+ }
2241
+ function ProgressBar({ percent, color }) {
2242
+ const WIDTH = 24;
2243
+ const filled = Math.round(percent / 100 * WIDTH);
2244
+ const empty = WIDTH - filled;
2245
+ return /* @__PURE__ */ jsxs10(Text11, { children: [
2246
+ /* @__PURE__ */ jsx10(Text11, { color, children: "\u2588".repeat(filled) }),
2247
+ /* @__PURE__ */ jsx10(Text11, { color: "#444444", children: "\u2591".repeat(empty) }),
2248
+ /* @__PURE__ */ jsxs10(Text11, { color, children: [
2249
+ " ",
2250
+ percent.toFixed(0),
2251
+ "%"
2252
+ ] })
2253
+ ] });
2254
+ }
2255
+ function PhaseSteps({ state }) {
2256
+ const step = (() => {
2257
+ if (state.phase === "idle") return 0;
2258
+ if (state.phase === "error") {
2259
+ const p2 = state.progress;
2260
+ if (p2?.type === "summary_delta" || p2?.type === "start") return 1;
2261
+ return 2;
2262
+ }
2263
+ if (state.phase === "done") return 3;
2264
+ const p = state.progress;
2265
+ if (!p || p.type === "start") return 0;
2266
+ if (p.type === "summary_delta") return 1;
2267
+ if (p.type === "fallback") return 2;
2268
+ return 2;
2269
+ })();
2270
+ const labels = ["\u542F\u52A8", "LLM \u6458\u8981", "\u5E94\u7528\u538B\u7F29", "\u5B8C\u6210"];
2271
+ return /* @__PURE__ */ jsx10(Box10, { children: labels.map((label, i) => {
2272
+ const isCurrent = i === step;
2273
+ const isDone = i < step;
2274
+ const marker = isDone ? "\u2713" : isCurrent ? "\u25B8" : "\xB7";
2275
+ const color = isDone ? "#00ff41" : isCurrent ? "#00ffff" : "#444444";
2276
+ return /* @__PURE__ */ jsxs10(Box10, { marginRight: 1, children: [
2277
+ /* @__PURE__ */ jsxs10(Text11, { color, children: [
2278
+ marker,
2279
+ " ",
2280
+ label
2281
+ ] }),
2282
+ i < labels.length - 1 && /* @__PURE__ */ jsx10(Text11, { color: "#444444", children: " \u2192 " })
2283
+ ] }, label);
2284
+ }) });
2285
+ }
2286
+ function SummaryPreview({ text, contentWidth }) {
2287
+ const MAX_LINES = 4;
2288
+ const MAX_LINE_WIDTH = Math.max(contentWidth - 6, 20);
2289
+ if (!text) {
2290
+ return /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: " (\u7B49\u5F85 LLM \u8F93\u51FA...)" });
2291
+ }
2292
+ const lines = wrapByWidth2(text, MAX_LINE_WIDTH);
2293
+ const kept = lines.slice(-MAX_LINES);
2294
+ return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: kept.map((line, i) => /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
2295
+ " ",
2296
+ line
2297
+ ] }, i)) });
2298
+ }
2299
+ function wrapByWidth2(text, maxWidth) {
2300
+ if (text === "") return [];
2301
+ if (maxWidth <= 0) return [text];
2302
+ const result = [];
2303
+ for (const para of text.split("\n")) {
2304
+ if (para.length === 0) {
2305
+ result.push("");
2306
+ continue;
2307
+ }
2308
+ let buf = "";
2309
+ let bufWidth = 0;
2310
+ for (const ch of para) {
2311
+ const cp2 = ch.codePointAt(0);
2312
+ const w = cp2 > 11904 ? 2 : 1;
2313
+ if (bufWidth + w > maxWidth && buf.length > 0) {
2314
+ result.push(buf);
2315
+ buf = ch;
2316
+ bufWidth = w;
2317
+ } else {
2318
+ buf += ch;
2319
+ bufWidth += w;
2320
+ }
2321
+ }
2322
+ if (buf.length > 0) result.push(buf);
2323
+ }
2324
+ return result;
2325
+ }
2326
+ function fmtNum(n) {
2327
+ return n.toLocaleString();
2328
+ }
2329
+ function CompactionProgress({ state, contentWidth }) {
2330
+ const percent = useMemo(() => estimateProgress(state), [state]);
2331
+ const phase = useMemo(() => phaseLabel(state), [state]);
2332
+ const summaryText = state.progress?.type === "summary_delta" ? state.progress.totalSoFar : state.progress?.type === "fallback" ? state.progress.fallbackSummary : state.phase === "done" && state.strategy === "summary" ? "" : "";
2333
+ const finalSummary = state.phase === "done" && state.strategy === "summary" ? state.progress?.type === "summary_delta" ? state.progress.totalSoFar : "" : summaryText;
2334
+ const titleColor = state.phase === "error" ? "#ff6347" : "#00ffff";
2335
+ return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
2336
+ /* @__PURE__ */ jsx10(Box10, { width: 1, backgroundColor: "#9b59b6", flexShrink: 0 }),
2337
+ /* @__PURE__ */ jsxs10(Box10, { flexGrow: 1, paddingLeft: 1, flexDirection: "column", children: [
2338
+ /* @__PURE__ */ jsxs10(Box10, { children: [
2339
+ /* @__PURE__ */ jsx10(Text11, { bold: true, color: titleColor, children: "\u{1F5DC} \u4E0A\u4E0B\u6587\u538B\u7F29" }),
2340
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
2341
+ state.phase === "running" && /* @__PURE__ */ jsxs10(Text11, { color: phase.color, children: [
2342
+ /* @__PURE__ */ jsx10(InkSpinner3, { type: "dots" }),
2343
+ " ",
2344
+ phase.text
2345
+ ] }),
2346
+ state.phase === "done" && /* @__PURE__ */ jsxs10(Text11, { color: phase.color, children: [
2347
+ phase.icon,
2348
+ " ",
2349
+ phase.text
2350
+ ] }),
2351
+ state.phase === "error" && /* @__PURE__ */ jsxs10(Text11, { color: phase.color, children: [
2352
+ phase.icon,
2353
+ " ",
2354
+ phase.text
2355
+ ] })
2356
+ ] }),
2357
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2358
+ /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: "\u8FDB\u5EA6: " }),
2359
+ /* @__PURE__ */ jsx10(ProgressBar, { percent, color: state.phase === "error" ? "#ff6347" : phase.color })
2360
+ ] }),
2361
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(PhaseSteps, { state }) }),
2362
+ (state.beforeTokens !== void 0 || state.droppedTurns !== void 0) && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
2363
+ state.droppedTurns !== void 0 && /* @__PURE__ */ jsxs10(Text11, { color: "#00ff41", children: [
2364
+ "\u{1F4CA} \u56DE\u5408: ",
2365
+ state.droppedTurns,
2366
+ " \u6298\u53E0 \u2192 ",
2367
+ state.keptTurns ?? 0,
2368
+ " \u4FDD\u7559"
2369
+ ] }),
2370
+ state.beforeTokens !== void 0 && /* @__PURE__ */ jsxs10(Text11, { color: "#ff9800", children: [
2371
+ "\u{1F4E6} token: ",
2372
+ fmtNum(state.beforeTokens),
2373
+ state.afterTokens !== void 0 && /* @__PURE__ */ jsxs10(Fragment2, { children: [
2374
+ " \u2192 ",
2375
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ff41", bold: true, children: fmtNum(state.afterTokens) }),
2376
+ /* @__PURE__ */ jsxs10(Text11, { color: "#808080", children: [
2377
+ " (\u8282\u7701 ",
2378
+ ((state.beforeTokens - state.afterTokens) / Math.max(state.beforeTokens, 1) * 100).toFixed(1),
2379
+ "%)"
2380
+ ] })
2381
+ ] })
2382
+ ] })
2383
+ ] }),
2384
+ (state.phase === "running" || state.phase === "done") && finalSummary && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
2385
+ /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: "\u2504\u2504\u2504 LLM \u6458\u8981\u9884\u89C8 \u2504\u2504\u2504" }),
2386
+ /* @__PURE__ */ jsx10(SummaryPreview, { text: finalSummary, contentWidth })
2387
+ ] }),
2388
+ state.phase === "done" && state.strategy === "fallback" && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "#ff9800", children: "\u26A0 LLM \u6458\u8981\u5931\u8D25\uFF0C\u4F7F\u7528\u672C\u5730\u515C\u5E95\u6458\u8981\u3002\u538B\u7F29\u5DF2\u751F\u6548\u4F46\u6458\u8981\u8D28\u91CF\u8F83\u4F4E\u3002" }) }),
2389
+ state.phase === "error" && state.errorMessage && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { color: "#ff6347", children: [
2390
+ "\u26A0 ",
2391
+ state.errorMessage
2392
+ ] }) })
2393
+ ] })
2394
+ ] }) });
2395
+ }
2396
+
1984
2397
  // package.json
1985
2398
  var package_default = {
1986
2399
  name: "dskcode",
1987
- version: "0.1.39",
2400
+ version: "0.1.41",
1988
2401
  repository: {
1989
2402
  type: "git",
1990
2403
  url: "git+https://github.com/Awu12277/deepseek-agent-cli/tree/main"
@@ -2020,9 +2433,8 @@ var package_default = {
2020
2433
  "lint:fix": "oxlint --fix src/ tests/",
2021
2434
  format: "prettier --write src/ tests/",
2022
2435
  "format:check": "prettier --check src/ tests/",
2023
- test: "vitest run",
2024
- "test:watch": "vitest",
2025
- "test:coverage": "vitest run --coverage",
2436
+ test: "vitest run --exclude 'tests/checkpoint.test.ts' --exclude 'tests/session-rewind.test.ts'",
2437
+ "test:checkpoint": "vitest run tests/checkpoint.test.ts tests/session-rewind.test.ts",
2026
2438
  prepublishOnly: "npm run build && npm run test"
2027
2439
  },
2028
2440
  dependencies: {
@@ -2033,7 +2445,8 @@ var package_default = {
2033
2445
  ink: "^7.1.0",
2034
2446
  "ink-spinner": "^5.0.0",
2035
2447
  "ink-text-input": "^6.0.0",
2036
- react: "^19.2.7"
2448
+ react: "^19.2.7",
2449
+ zod: "^3.25.76"
2037
2450
  },
2038
2451
  devDependencies: {
2039
2452
  "@types/node": "^22.14.0",
@@ -2267,6 +2680,246 @@ function buildApiMessages(systemPrompt, history) {
2267
2680
  ];
2268
2681
  }
2269
2682
 
2683
+ // src/agent/compactor.ts
2684
+ var DEFAULT_AUTO_COMPACT_RATIO = 0.85;
2685
+ var DEFAULT_PRESERVE_ROUNDS = 6;
2686
+ var DEFAULT_MIN_TURNS_TO_COMPACT = 8;
2687
+ var SUMMARY_SENTINEL = "[history-summary]";
2688
+ var SUMMARY_SYSTEM_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u5BF9\u8BDD\u6458\u8981\u52A9\u624B\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u628A\u591A\u8F6E\u5BF9\u8BDD\u5386\u53F2\u538B\u7F29\u6210\u7ED3\u6784\u5316\u4E2D\u6587\u6458\u8981\u3002
2689
+
2690
+ \u6458\u8981\u5FC5\u987B\u4FDD\u7559\u4EE5\u4E0B\u4FE1\u606F\uFF08\u6309\u91CD\u8981\u6027\u6392\u5E8F\uFF09\uFF1A
2691
+ 1. \u7528\u6237\u7684\u6838\u5FC3\u76EE\u6807\u4E0E\u6700\u7EC8\u610F\u56FE
2692
+ 2. \u5DF2\u5B8C\u6210\u7684\u5173\u952E\u51B3\u7B56\uFF08\u5982\u9009\u4E86\u54EA\u4E2A\u65B9\u6848\u3001\u6539\u4E86\u54EA\u4E9B\u6587\u4EF6\u3001\u4E3A\u4EC0\u4E48\u8FD9\u4E48\u6539\uFF09
2693
+ 3. \u6D89\u53CA\u7684\u5177\u4F53\u6587\u4EF6\u8DEF\u5F84\uFF08\u7EDD\u5BF9/\u76F8\u5BF9\u8DEF\u5F84\u90FD\u8981\u4FDD\u7559\uFF09
2694
+ 4. \u51FA\u73B0\u8FC7\u7684\u9519\u8BEF\u4FE1\u606F\u4E0E\u89E3\u51B3\u65B9\u6848
2695
+ 5. \u5C1A\u672A\u5B8C\u6210\u7684\u5DE5\u4F5C / \u5F85\u529E\u4E8B\u9879
2696
+ 6. \u91CD\u8981\u7684\u6280\u672F\u7EA6\u675F\uFF08\u5982"\u4E0D\u80FD\u7528 XX \u5E93"\uFF09
2697
+
2698
+ \u683C\u5F0F\u8981\u6C42\uFF1A
2699
+ - \u7528\u7EAF\u6587\u672C\uFF0C\u4E0D\u8981 markdown \u6807\u9898\u5206\u7EA7
2700
+ - \u6BCF\u6761\u4FE1\u606F\u72EC\u7ACB\u6210\u884C\uFF0C\u65B9\u4FBF\u540E\u7EED\u6309\u884C\u89E3\u6790
2701
+ - \u603B\u957F\u5EA6\u63A7\u5236\u5728 2000 \u5B57\u4EE5\u5185
2702
+ - \u4E0D\u9700\u8981\u5BA2\u5957\u8BDD\u3001\u4E0D\u9700\u8981"\u4EE5\u4E0B\u662F\u6458\u8981"\u7B49\u5F00\u5934
2703
+ `;
2704
+ var PER_MESSAGE_TEXT_LIMIT = 500;
2705
+ var FALLBACK_USER_PREVIEW = 60;
2706
+ function estimateMessageTokens2(msg) {
2707
+ let text = msg.content;
2708
+ if (msg.toolCalls) {
2709
+ for (const tc of msg.toolCalls) {
2710
+ text += tc.name + tc.arguments;
2711
+ }
2712
+ }
2713
+ return estimateTokens(text) + 10;
2714
+ }
2715
+ function estimateMessagesTokens(messages) {
2716
+ let sum = 0;
2717
+ for (const m of messages) sum += estimateMessageTokens2(m);
2718
+ return sum;
2719
+ }
2720
+ function getContextStats(messages, contextWindow) {
2721
+ const estimatedTokens = estimateMessagesTokens(messages);
2722
+ const ratio = contextWindow > 0 ? estimatedTokens / contextWindow : 0;
2723
+ return {
2724
+ messageCount: messages.length,
2725
+ estimatedTokens,
2726
+ contextWindow,
2727
+ ratio,
2728
+ headroom: contextWindow - estimatedTokens
2729
+ };
2730
+ }
2731
+ function shouldAutoCompact(messages, opts) {
2732
+ const turns = groupIntoTurns(messages);
2733
+ const minTurns = opts.minTurnsToCompact ?? DEFAULT_MIN_TURNS_TO_COMPACT;
2734
+ if (turns.length < minTurns) return false;
2735
+ const ratio = opts.autoCompactRatio ?? DEFAULT_AUTO_COMPACT_RATIO;
2736
+ const stats = getContextStats(messages, opts.contextWindow);
2737
+ return stats.ratio >= ratio;
2738
+ }
2739
+ function turnsToPromptText(turns) {
2740
+ const lines = [];
2741
+ for (let i = 0; i < turns.length; i++) {
2742
+ const turn = turns[i];
2743
+ lines.push(`--- \u7B2C ${i + 1} \u56DE\u5408 ---`);
2744
+ for (const msg of turn) {
2745
+ const head = `[${msg.role}]`;
2746
+ const body = msg.content.length > PER_MESSAGE_TEXT_LIMIT ? msg.content.slice(0, PER_MESSAGE_TEXT_LIMIT) + `\u2026(\u5DF2\u622A\u65AD,\u539F\u957F ${msg.content.length})` : msg.content;
2747
+ lines.push(`${head} ${body}`);
2748
+ if (msg.toolCalls) {
2749
+ for (const tc of msg.toolCalls) {
2750
+ lines.push(` tool_call: ${tc.name}(${tc.arguments})`);
2751
+ }
2752
+ }
2753
+ }
2754
+ }
2755
+ return lines.join("\n");
2756
+ }
2757
+ function fallbackSummary(turns) {
2758
+ const lines = ["\u3010\u672C\u5730\u6458\u8981\uFF08LLM \u8C03\u7528\u5931\u8D25\u65F6\u515C\u5E95\uFF09\u3011"];
2759
+ for (let i = 0; i < turns.length; i++) {
2760
+ const turn = turns[i];
2761
+ const userMsg = turn.find((m) => m.role === "user");
2762
+ if (userMsg) {
2763
+ const preview = userMsg.content.slice(0, FALLBACK_USER_PREVIEW);
2764
+ lines.push(`\u56DE\u5408${i + 1} \u7528\u6237: ${preview}`);
2765
+ }
2766
+ const toolCalls = [];
2767
+ for (const m of turn) {
2768
+ if (m.toolCalls) for (const tc of m.toolCalls) toolCalls.push({ name: tc.name, argsRaw: tc.arguments });
2769
+ }
2770
+ if (toolCalls.length > 0) {
2771
+ const summaries = toolCalls.map((tc) => {
2772
+ const parsed = parseToolCallArgs(tc.argsRaw);
2773
+ if (!parsed.ok) {
2774
+ return `${tc.name}(\u89E3\u6790\u5931\u8D25)`;
2775
+ }
2776
+ return templateToolCall(tc.name, maskSensitive(parsed.args));
2777
+ });
2778
+ lines.push(` \u8C03\u7528\u5DE5\u5177: ${summaries.join("; ")}`);
2779
+ }
2780
+ }
2781
+ return lines.join("\n");
2782
+ }
2783
+ async function summarizeOldTurns(oldTurns, opts) {
2784
+ if (oldTurns.length === 0) return "";
2785
+ const promptText = turnsToPromptText(oldTurns);
2786
+ try {
2787
+ const messages = [
2788
+ { role: "system", content: SUMMARY_SYSTEM_PROMPT },
2789
+ {
2790
+ role: "user",
2791
+ content: `\u8BF7\u628A\u4EE5\u4E0B ${oldTurns.length} \u8F6E\u5BF9\u8BDD\u5386\u53F2\u538B\u7F29\u6210\u7ED3\u6784\u5316\u6458\u8981\uFF1A
2792
+
2793
+ ${promptText}`
2794
+ }
2795
+ ];
2796
+ const stream = opts.provider.chat(messages, {
2797
+ ...opts.signal ? { signal: opts.signal } : {},
2798
+ // 摘要场景压低温度,输出更稳定
2799
+ temperature: 0.2
2800
+ });
2801
+ let summary = "";
2802
+ for await (const chunk of stream) {
2803
+ if (chunk.content) {
2804
+ summary += chunk.content;
2805
+ if (opts.onProgress) {
2806
+ try {
2807
+ opts.onProgress({
2808
+ type: "summary_delta",
2809
+ delta: chunk.content,
2810
+ totalSoFar: summary
2811
+ });
2812
+ } catch {
2813
+ }
2814
+ }
2815
+ }
2816
+ }
2817
+ if (!summary.trim()) {
2818
+ const fb = fallbackSummary(oldTurns);
2819
+ if (opts.onProgress) {
2820
+ try {
2821
+ opts.onProgress({
2822
+ type: "fallback",
2823
+ reason: "LLM \u8FD4\u56DE\u7A7A\u5B57\u7B26\u4E32",
2824
+ fallbackSummary: fb
2825
+ });
2826
+ } catch {
2827
+ }
2828
+ }
2829
+ return fb;
2830
+ }
2831
+ return summary.trim();
2832
+ } catch (err) {
2833
+ const errMsg = err instanceof Error ? err.message : String(err);
2834
+ const fb = fallbackSummary(oldTurns);
2835
+ if (opts.onProgress) {
2836
+ try {
2837
+ opts.onProgress({
2838
+ type: "fallback",
2839
+ reason: errMsg,
2840
+ fallbackSummary: fb
2841
+ });
2842
+ } catch {
2843
+ }
2844
+ }
2845
+ return fb;
2846
+ }
2847
+ }
2848
+ function buildSummaryMessage(summary) {
2849
+ return {
2850
+ role: "system",
2851
+ content: `${SUMMARY_SENTINEL}
2852
+ ${summary}`
2853
+ };
2854
+ }
2855
+ async function compactContext(messages, opts) {
2856
+ const beforeTokens = estimateMessagesTokens(messages);
2857
+ const turns = groupIntoTurns(messages);
2858
+ const minTurns = opts.minTurnsToCompact ?? DEFAULT_MIN_TURNS_TO_COMPACT;
2859
+ const preserveRounds = opts.preserveRecentRounds ?? DEFAULT_PRESERVE_ROUNDS;
2860
+ if (turns.length < minTurns) {
2861
+ return {
2862
+ messages: messages.slice(),
2863
+ summary: "",
2864
+ droppedTurns: 0,
2865
+ keptTurns: turns.length,
2866
+ beforeTokens,
2867
+ afterTokens: beforeTokens
2868
+ };
2869
+ }
2870
+ if (turns.length <= preserveRounds) {
2871
+ return {
2872
+ messages: messages.slice(),
2873
+ summary: "",
2874
+ droppedTurns: 0,
2875
+ keptTurns: turns.length,
2876
+ beforeTokens,
2877
+ afterTokens: beforeTokens
2878
+ };
2879
+ }
2880
+ const splitAt = turns.length - preserveRounds;
2881
+ const oldTurns = turns.slice(0, splitAt);
2882
+ const keptTurns = turns.slice(splitAt);
2883
+ if (opts.onProgress) {
2884
+ try {
2885
+ opts.onProgress({
2886
+ type: "start",
2887
+ droppedTurns: oldTurns.length,
2888
+ beforeTokens
2889
+ });
2890
+ } catch {
2891
+ }
2892
+ }
2893
+ const summary = await summarizeOldTurns(oldTurns, opts);
2894
+ const newMessages = [buildSummaryMessage(summary)];
2895
+ for (const turn of keptTurns) {
2896
+ for (const msg of turn) {
2897
+ newMessages.push(msg);
2898
+ }
2899
+ }
2900
+ const afterTokens = estimateMessagesTokens(newMessages);
2901
+ if (opts.onProgress) {
2902
+ try {
2903
+ opts.onProgress({
2904
+ type: "done",
2905
+ droppedTurns: oldTurns.length,
2906
+ keptTurns: keptTurns.length,
2907
+ beforeTokens,
2908
+ afterTokens
2909
+ });
2910
+ } catch {
2911
+ }
2912
+ }
2913
+ return {
2914
+ messages: newMessages,
2915
+ summary,
2916
+ droppedTurns: oldTurns.length,
2917
+ keptTurns: keptTurns.length,
2918
+ beforeTokens,
2919
+ afterTokens
2920
+ };
2921
+ }
2922
+
2270
2923
  // src/tool/registry.ts
2271
2924
  var ToolRegistry = class {
2272
2925
  #tools = /* @__PURE__ */ new Map();
@@ -2433,6 +3086,326 @@ var ToolRegistry = class {
2433
3086
  }
2434
3087
  };
2435
3088
 
3089
+ // src/tool/zod-schema-validator.ts
3090
+ import { z } from "zod";
3091
+ function zodSafeValidate(args, schema) {
3092
+ const result = schema.safeParse(args);
3093
+ if (result.success) {
3094
+ return { ok: true, issues: [] };
3095
+ }
3096
+ const issues = result.error.issues.map(
3097
+ (zi) => zodIssueToValidationIssue(zi, args)
3098
+ );
3099
+ return { ok: issues.length === 0, issues };
3100
+ }
3101
+ function zodIssueToValidationIssue(zi, rootArgs) {
3102
+ const path = formatPath(zi.path);
3103
+ const expected = formatExpected(zi);
3104
+ const received = describeReceived(rootArgs, zi.path);
3105
+ const message = formatMessage(path, expected, received, zi);
3106
+ return { path, expected, received, message };
3107
+ }
3108
+ function formatPath(path) {
3109
+ if (path.length === 0) return "$";
3110
+ let out = "$";
3111
+ for (const seg of path) {
3112
+ if (typeof seg === "number") {
3113
+ out += `[${seg}]`;
3114
+ } else if (typeof seg === "string") {
3115
+ out += `.${seg}`;
3116
+ } else {
3117
+ out += "[]";
3118
+ }
3119
+ }
3120
+ return out;
3121
+ }
3122
+ function formatExpected(zi) {
3123
+ switch (zi.code) {
3124
+ case "invalid_type": {
3125
+ const exp = zi.expected;
3126
+ return exp;
3127
+ }
3128
+ case "too_small": {
3129
+ const t = zi.type;
3130
+ const min = zi.minimum;
3131
+ if (t === "string") return `length >= ${min}`;
3132
+ if (t === "array") return `length >= ${min}`;
3133
+ if (t === "number") return `>= ${min}`;
3134
+ return `>= ${min}`;
3135
+ }
3136
+ case "too_big": {
3137
+ const t = zi.type;
3138
+ const max = zi.maximum;
3139
+ if (t === "string") return `length <= ${max}`;
3140
+ if (t === "array") return `length <= ${max}`;
3141
+ if (t === "number") return `<= ${max}`;
3142
+ return `<= ${max}`;
3143
+ }
3144
+ case "invalid_enum_value": {
3145
+ const opts = zi.options;
3146
+ return `enum[${opts.map((o) => JSON.stringify(o)).join(",")}]`;
3147
+ }
3148
+ case "invalid_string": {
3149
+ const v = zi.validation;
3150
+ return `string ${v}`;
3151
+ }
3152
+ case "unrecognized_keys": {
3153
+ const keys = zi.keys;
3154
+ return `\u672A\u5728 schema \u4E2D\u5B9A\u4E49: ${keys.join(",")}`;
3155
+ }
3156
+ case "invalid_literal": {
3157
+ const exp = zi.expected;
3158
+ return `literal[${JSON.stringify(exp)}]`;
3159
+ }
3160
+ default:
3161
+ return zi.code;
3162
+ }
3163
+ }
3164
+ function describeReceived(rootArgs, path) {
3165
+ let cur = rootArgs;
3166
+ for (const seg of path) {
3167
+ if (cur === null || cur === void 0) return "undefined";
3168
+ if (typeof cur !== "object") return "undefined";
3169
+ if (typeof seg === "number") {
3170
+ if (!Array.isArray(cur)) return "undefined";
3171
+ cur = cur[seg];
3172
+ } else if (typeof seg === "string") {
3173
+ cur = cur[seg];
3174
+ } else {
3175
+ return "undefined";
3176
+ }
3177
+ }
3178
+ if (cur === null) return "null";
3179
+ if (cur === void 0) return "undefined";
3180
+ if (typeof cur === "string") {
3181
+ return cur.length > 60 ? JSON.stringify(cur.slice(0, 57) + "...") : JSON.stringify(cur);
3182
+ }
3183
+ if (typeof cur === "number" || typeof cur === "boolean") return JSON.stringify(cur);
3184
+ if (Array.isArray(cur)) return `array(len=${cur.length})`;
3185
+ if (typeof cur === "object") {
3186
+ const keys = Object.keys(cur);
3187
+ return `object{keys=${keys.length}}`;
3188
+ }
3189
+ return typeof cur;
3190
+ }
3191
+ function formatMessage(path, expected, received, zi) {
3192
+ if (zi.code === "invalid_type") {
3193
+ return `${path} \u5E94\u4E3A ${expected}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3194
+ }
3195
+ if (zi.code === "too_small") {
3196
+ const t = zi.type;
3197
+ const min = zi.minimum;
3198
+ if (t === "string") return `${path} \u957F\u5EA6\u5E94 >= ${min}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3199
+ if (t === "array") return `${path} \u957F\u5EA6\u5E94 >= ${min}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3200
+ if (t === "number") return `${path} \u5E94 >= ${min}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3201
+ return `${path} \u5E94 >= ${min}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3202
+ }
3203
+ if (zi.code === "too_big") {
3204
+ const t = zi.type;
3205
+ const max = zi.maximum;
3206
+ if (t === "string") return `${path} \u957F\u5EA6\u5E94 <= ${max}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3207
+ if (t === "array") return `${path} \u957F\u5EA6\u5E94 <= ${max}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3208
+ if (t === "number") return `${path} \u5E94 <= ${max}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3209
+ return `${path} \u5E94 <= ${max}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3210
+ }
3211
+ if (zi.code === "invalid_enum_value") {
3212
+ return `${path} \u5E94\u4E3A enum \u503C\u4E4B\u4E00\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3213
+ }
3214
+ if (zi.code === "unrecognized_keys") {
3215
+ const keys = zi.keys;
3216
+ return `${path} \u4E0D\u5141\u8BB8\u952E: ${keys.join(", ")}`;
3217
+ }
3218
+ if (zi.code === "invalid_literal") {
3219
+ return `${path} \u5E94\u4E3A ${expected}\uFF0C\u5B9E\u9645\u4E3A ${received}`;
3220
+ }
3221
+ if (zi.code === "invalid_string") {
3222
+ return `${path} \u4E0D\u5339\u914D ${expected}`;
3223
+ }
3224
+ return `${path} ${zi.message}\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${received}\uFF09`;
3225
+ }
3226
+
3227
+ // src/tool/schema-validator.ts
3228
+ function validateArgs(args, schema) {
3229
+ if (isZodSchema(schema)) {
3230
+ return zodSafeValidate(args, schema);
3231
+ }
3232
+ const issues = [];
3233
+ if (!isPlainObject2(schema)) {
3234
+ return { ok: true, issues };
3235
+ }
3236
+ if (schema.type !== void 0 && schema.type !== "object") {
3237
+ return { ok: true, issues };
3238
+ }
3239
+ validateObject(args, schema, "$", issues);
3240
+ return { ok: issues.length === 0, issues };
3241
+ }
3242
+ function isZodSchema(schema) {
3243
+ if (schema === null || typeof schema !== "object") return false;
3244
+ const obj = schema;
3245
+ return "_def" in obj && typeof obj.parse === "function" && typeof obj.safeParse === "function";
3246
+ }
3247
+ function validateObject(value, schema, path, issues) {
3248
+ if (!isPlainObject2(value)) {
3249
+ issues.push({
3250
+ path,
3251
+ expected: "object",
3252
+ received: describe(value),
3253
+ message: `${path} \u5E94\u4E3A object\uFF0C\u5B9E\u9645\u4E3A ${describe(value)}`
3254
+ });
3255
+ return;
3256
+ }
3257
+ for (const key of schema.required ?? []) {
3258
+ if (!(key in value)) {
3259
+ issues.push({
3260
+ path: `${path}.${key}`,
3261
+ expected: "present",
3262
+ received: "missing",
3263
+ message: `${path}.${key} \u662F\u5FC5\u586B\u5B57\u6BB5`
3264
+ });
3265
+ }
3266
+ }
3267
+ const props = schema.properties ?? {};
3268
+ const additional = schema.additionalProperties;
3269
+ for (const [k, v] of Object.entries(value)) {
3270
+ const propSchema = props[k];
3271
+ if (propSchema === void 0) {
3272
+ if (additional === false) {
3273
+ issues.push({
3274
+ path: `${path}.${k}`,
3275
+ expected: "\u672A\u5728 schema \u4E2D\u5B9A\u4E49",
3276
+ received: describe(v),
3277
+ message: `${path}.${k} \u4E0D\u5728\u5141\u8BB8\u5B57\u6BB5\u4E2D`
3278
+ });
3279
+ }
3280
+ continue;
3281
+ }
3282
+ validateProperty(v, propSchema, `${path}.${k}`, issues);
3283
+ }
3284
+ }
3285
+ function validateProperty(value, schema, path, issues) {
3286
+ if (schema.type !== void 0) {
3287
+ const actual = jsonTypeOf(value);
3288
+ if (!typeMatches(actual, schema.type)) {
3289
+ issues.push({
3290
+ path,
3291
+ expected: schema.type,
3292
+ received: describe(value),
3293
+ message: `${path} \u5E94\u4E3A ${schema.type}\uFF0C\u5B9E\u9645\u4E3A ${actual}`
3294
+ });
3295
+ return;
3296
+ }
3297
+ }
3298
+ if (schema.enum && schema.enum.length > 0) {
3299
+ if (!isScalar(value)) {
3300
+ issues.push({
3301
+ path,
3302
+ expected: `enum[${schema.enum.map((e) => JSON.stringify(e)).join(",")}]`,
3303
+ received: describe(value),
3304
+ message: `${path} \u5E94\u4E3A enum \u503C\u4E4B\u4E00\uFF0C\u5B9E\u9645\u4E3A ${describe(value)}`
3305
+ });
3306
+ } else if (!schema.enum.includes(value)) {
3307
+ issues.push({
3308
+ path,
3309
+ expected: `enum[${schema.enum.map((e) => JSON.stringify(e)).join(",")}]`,
3310
+ received: describe(value),
3311
+ message: `${path} \u5E94\u4E3A enum \u503C\u4E4B\u4E00\uFF0C\u5B9E\u9645\u4E3A ${JSON.stringify(value)}`
3312
+ });
3313
+ }
3314
+ }
3315
+ if (typeof value === "number") {
3316
+ if (schema.minimum !== void 0 && value < schema.minimum) {
3317
+ issues.push({
3318
+ path,
3319
+ expected: `>= ${schema.minimum}`,
3320
+ received: String(value),
3321
+ message: `${path} \u5E94 >= ${schema.minimum}\uFF0C\u5B9E\u9645\u4E3A ${value}`
3322
+ });
3323
+ }
3324
+ if (schema.maximum !== void 0 && value > schema.maximum) {
3325
+ issues.push({
3326
+ path,
3327
+ expected: `<= ${schema.maximum}`,
3328
+ received: String(value),
3329
+ message: `${path} \u5E94 <= ${schema.maximum}\uFF0C\u5B9E\u9645\u4E3A ${value}`
3330
+ });
3331
+ }
3332
+ }
3333
+ if (typeof value === "string") {
3334
+ if (schema.minLength !== void 0 && value.length < schema.minLength) {
3335
+ issues.push({
3336
+ path,
3337
+ expected: `length >= ${schema.minLength}`,
3338
+ received: `length ${value.length}`,
3339
+ message: `${path} \u957F\u5EA6\u5E94 >= ${schema.minLength}\uFF0C\u5B9E\u9645\u4E3A ${value.length}`
3340
+ });
3341
+ }
3342
+ if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
3343
+ issues.push({
3344
+ path,
3345
+ expected: `length <= ${schema.maxLength}`,
3346
+ received: `length ${value.length}`,
3347
+ message: `${path} \u957F\u5EA6\u5E94 <= ${schema.maxLength}\uFF0C\u5B9E\u9645\u4E3A ${value.length}`
3348
+ });
3349
+ }
3350
+ if (schema.pattern !== void 0) {
3351
+ try {
3352
+ const re = new RegExp(schema.pattern);
3353
+ if (!re.test(value)) {
3354
+ issues.push({
3355
+ path,
3356
+ expected: `pattern ${schema.pattern}`,
3357
+ received: describe(value),
3358
+ message: `${path} \u4E0D\u5339\u914D pattern ${schema.pattern}`
3359
+ });
3360
+ }
3361
+ } catch {
3362
+ }
3363
+ }
3364
+ }
3365
+ if (schema.properties !== void 0) {
3366
+ validateObject(value, schema, path, issues);
3367
+ }
3368
+ if (schema.items !== void 0 && Array.isArray(value)) {
3369
+ for (let i = 0; i < value.length; i++) {
3370
+ validateProperty(value[i], schema.items, `${path}[${i}]`, issues);
3371
+ }
3372
+ }
3373
+ }
3374
+ function isPlainObject2(v) {
3375
+ return typeof v === "object" && v !== null && !Array.isArray(v);
3376
+ }
3377
+ function isScalar(v) {
3378
+ return v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean";
3379
+ }
3380
+ function jsonTypeOf(v) {
3381
+ if (v === null) return "null";
3382
+ if (Array.isArray(v)) return "array";
3383
+ if (typeof v === "object") return "object";
3384
+ if (typeof v === "number") return Number.isInteger(v) ? "integer" : "number";
3385
+ return typeof v;
3386
+ }
3387
+ function typeMatches(actual, expected) {
3388
+ if (actual === expected) return true;
3389
+ if (expected === "integer" && actual === "number") return true;
3390
+ if (expected === "number" && actual === "integer") return true;
3391
+ return false;
3392
+ }
3393
+ function describe(v) {
3394
+ if (v === null) return "null";
3395
+ if (v === void 0) return "undefined";
3396
+ if (typeof v === "string") {
3397
+ const s = v.length > 60 ? v.slice(0, 57) + "..." : v;
3398
+ return JSON.stringify(s);
3399
+ }
3400
+ if (typeof v === "number" || typeof v === "boolean") return JSON.stringify(v);
3401
+ if (Array.isArray(v)) return `array(len=${v.length})`;
3402
+ if (typeof v === "object") {
3403
+ const keys = Object.keys(v);
3404
+ return `object{keys=${keys.length}}`;
3405
+ }
3406
+ return typeof v;
3407
+ }
3408
+
2436
3409
  // src/agent/tool-executor.ts
2437
3410
  var ToolExecutor = class {
2438
3411
  /** 工具注册表(只读引用) */
@@ -2559,11 +3532,38 @@ var ToolExecutor = class {
2559
3532
  record: { name: toolName, success: false, error: "TOOL_NOT_FOUND", timestamp }
2560
3533
  };
2561
3534
  }
2562
- let toolArgs;
2563
- try {
2564
- toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {};
2565
- } catch {
2566
- toolArgs = {};
3535
+ const parsed = parseToolCallArgs(tc.arguments ?? "");
3536
+ if (!parsed.ok) {
3537
+ if (parsed.reason === "PARTIAL") {
3538
+ const errMsg2 = `\u5DE5\u5177 "${toolName}" \u53C2\u6570\u5C1A\u672A\u6536\u5168\uFF0C\u672C\u8F6E\u8DF3\u8FC7\uFF08\u6D41\u5F0F\u5206\u7247\u5BFC\u81F4\uFF09\uFF1A${parsed.hint}`;
3539
+ return {
3540
+ item: {
3541
+ name: toolName,
3542
+ callId: tc.id,
3543
+ result: { success: false, data: errMsg2, error: "PARTIAL_JSON" }
3544
+ },
3545
+ record: { name: toolName, success: false, error: "PARTIAL_JSON", timestamp }
3546
+ };
3547
+ }
3548
+ const errMsg = `\u5DE5\u5177 "${toolName}" \u53C2\u6570\u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${parsed.error}
3549
+ \u539F\u6587: ${parsed.raw.slice(0, 200)}`;
3550
+ return {
3551
+ item: {
3552
+ name: toolName,
3553
+ callId: tc.id,
3554
+ result: { success: false, data: errMsg, error: "INVALID_JSON" }
3555
+ },
3556
+ record: { name: toolName, success: false, error: "INVALID_JSON", timestamp }
3557
+ };
3558
+ }
3559
+ const toolArgs = parsed.args;
3560
+ let schemaIssues;
3561
+ const schemaToCheck = tool.schema ?? tool.parameters;
3562
+ if (schemaToCheck) {
3563
+ const validation = validateArgs(toolArgs, schemaToCheck);
3564
+ if (!validation.ok) {
3565
+ schemaIssues = validation.issues;
3566
+ }
2567
3567
  }
2568
3568
  const gateResult = await this.#gate.check(toolName, toolArgs);
2569
3569
  if (!gateResult) {
@@ -2583,10 +3583,29 @@ var ToolExecutor = class {
2583
3583
  }
2584
3584
  }
2585
3585
  try {
2586
- const result = await this.#invokeTool(tool, toolArgs);
3586
+ let result = await this.#invokeTool(tool, toolArgs);
3587
+ if (schemaIssues && schemaIssues.length > 0) {
3588
+ const issuesText = schemaIssues.map((i) => ` - ${i.message}`).join("\n");
3589
+ const tag = result.success ? "[schema \u8B66\u544A]" : "[schema \u95EE\u9898]";
3590
+ result = {
3591
+ ...result,
3592
+ data: `${result.data}
3593
+
3594
+ ${tag} \u53C2\u6570\u4E0D\u7B26\u5408 schema\uFF1A
3595
+ ${issuesText}`,
3596
+ issues: schemaIssues
3597
+ };
3598
+ }
3599
+ const record = {
3600
+ name: toolName,
3601
+ success: result.success,
3602
+ error: result.error,
3603
+ timestamp,
3604
+ ...schemaIssues ? { issues: schemaIssues } : {}
3605
+ };
2587
3606
  return {
2588
3607
  item: { name: toolName, callId: tc.id, result },
2589
- record: { name: toolName, success: result.success, error: result.error, timestamp }
3608
+ record
2590
3609
  };
2591
3610
  } catch (err) {
2592
3611
  const message = err instanceof Error ? err.message : String(err);
@@ -2685,7 +3704,7 @@ var Reflector = class {
2685
3704
  const reflections = [];
2686
3705
  for (const item of items) {
2687
3706
  if (item.result.success) continue;
2688
- const r = this.#ruleRepeatedFailure(item) ?? this.#ruleFileNotFound(item) ?? this.#ruleTextNotFound(item) ?? this.#rulePermissionDenied(item) ?? this.#ruleOutOfWriteRoot(item, ctx.writeRoots);
3707
+ const r = this.#ruleRepeatedFailure(item) ?? this.#ruleFileNotFound(item) ?? this.#ruleTextNotFound(item) ?? this.#rulePermissionDenied(item) ?? this.#ruleOutOfWriteRoot(item, ctx.writeRoots) ?? this.#ruleInvalidArgs(item);
2689
3708
  if (r) reflections.push(r);
2690
3709
  if (reflections.length >= this.#maxReflections) break;
2691
3710
  }
@@ -2827,6 +3846,25 @@ var Reflector = class {
2827
3846
  if (text.length <= this.#maxHintChars) return text;
2828
3847
  return text.slice(0, this.#maxHintChars - 3) + "...";
2829
3848
  }
3849
+ /**
3850
+ * R5 INVALID_ARGS:参数不符合工具的 JSON Schema。
3851
+ *
3852
+ * 阶段 2 新增:tool-executor 校验后会把 issues 放进 result.issues,
3853
+ * 这里读取并生成反思。注意是 warn-only,不阻塞工具继续执行。
3854
+ *
3855
+ * @pure 仅读入参
3856
+ */
3857
+ #ruleInvalidArgs(item) {
3858
+ const issues = item.result.issues;
3859
+ if (!issues || issues.length === 0) return null;
3860
+ const shown = issues.slice(0, 3).map((i) => i.message).join("; ");
3861
+ const more = issues.length > 3 ? `\uFF08\u53E6\u6709 ${issues.length - 3} \u6761\uFF09` : "";
3862
+ return {
3863
+ category: "invalid_args",
3864
+ toolName: item.name,
3865
+ hint: `\`${item.name}\` \u53C2\u6570\u4E0D\u7B26\u5408 schema\uFF1A${shown}${more}\u3002\u8BF7\u68C0\u67E5\u5FC5\u586B\u5B57\u6BB5\u3001\u7C7B\u578B\u4E0E enum \u767D\u540D\u5355\u3002`
3866
+ };
3867
+ }
2830
3868
  };
2831
3869
 
2832
3870
  // src/agent/tool-definitions.ts
@@ -3342,7 +4380,12 @@ async function restoreToClean(cwd) {
3342
4380
  async function discardCheckpoint(checkpoint) {
3343
4381
  if (!checkpoint.isGitRepo || !checkpoint.stashSha) return;
3344
4382
  const { cwd, stashSha } = checkpoint;
3345
- const shas = await listStashShas(cwd);
4383
+ let shas;
4384
+ try {
4385
+ shas = await listStashShas(cwd);
4386
+ } catch {
4387
+ return;
4388
+ }
3346
4389
  const idx = shas.indexOf(stashSha);
3347
4390
  if (idx < 0) return;
3348
4391
  try {
@@ -3539,11 +4582,19 @@ var ConversationLogger = class _ConversationLogger {
3539
4582
  this.log({ ts: Date.now(), type: "reasoning", content, round });
3540
4583
  }
3541
4584
  /** 记录工具调用 */
3542
- logToolCall(name, callId, args, round) {
3543
- this.log({ ts: Date.now(), type: "tool_call", name, callId, arguments: args, round });
4585
+ logToolCall(name, callId, args, round, parsedArgs) {
4586
+ this.log({
4587
+ ts: Date.now(),
4588
+ type: "tool_call",
4589
+ name,
4590
+ callId,
4591
+ arguments: args,
4592
+ ...parsedArgs !== void 0 ? { parsedArgs } : {},
4593
+ round
4594
+ });
3544
4595
  }
3545
4596
  /** 记录工具结果 */
3546
- logToolResult(name, callId, success, data, error, elapsed, round) {
4597
+ logToolResult(name, callId, success, data, error, elapsed, round, schemaIssues) {
3547
4598
  this.log({
3548
4599
  ts: Date.now(),
3549
4600
  type: "tool_result",
@@ -3553,6 +4604,7 @@ var ConversationLogger = class _ConversationLogger {
3553
4604
  data: truncate(data),
3554
4605
  ...error ? { error } : {},
3555
4606
  ...elapsed !== void 0 ? { elapsed } : {},
4607
+ ...schemaIssues ? { schemaIssues } : {},
3556
4608
  round
3557
4609
  });
3558
4610
  }
@@ -3716,13 +4768,17 @@ var Session = class _Session {
3716
4768
  maxToolRounds: options?.maxToolRounds ?? 20,
3717
4769
  reservedForOutput: options?.reservedForOutput ?? 4096,
3718
4770
  preserveRecentRounds: options?.preserveRecentRounds ?? 10,
4771
+ autoCompactRatio: options?.autoCompactRatio ?? DEFAULT_AUTO_COMPACT_RATIO,
4772
+ preserveRecentRoundsOnCompact: options?.preserveRecentRoundsOnCompact ?? DEFAULT_PRESERVE_ROUNDS,
4773
+ minTurnsToCompact: options?.minTurnsToCompact ?? DEFAULT_MIN_TURNS_TO_COMPACT,
3719
4774
  projectContext: options?.projectContext,
3720
4775
  gate: options?.gate ?? new AlwaysAllowGate(),
3721
4776
  writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],
3722
4777
  enableCheckpoint: options?.enableCheckpoint ?? true,
3723
4778
  enableLog: options?.enableLog ?? true,
3724
4779
  enableReflection: options?.enableReflection !== false,
3725
- enableHarness: options?.enableHarness !== false
4780
+ enableHarness: options?.enableHarness !== false,
4781
+ enableAutoCompact: options?.enableAutoCompact !== false
3726
4782
  };
3727
4783
  this.#sessionId = options?.sessionId ?? SessionStore.newId();
3728
4784
  this.#store = options?.store === false ? null : options?.store ?? new SessionStore();
@@ -3823,6 +4879,19 @@ var Session = class _Session {
3823
4879
  } catch {
3824
4880
  }
3825
4881
  }
4882
+ if (this.#options.enableAutoCompact) {
4883
+ const compaction = await this.#maybeAutoCompact();
4884
+ if (compaction) {
4885
+ yield {
4886
+ type: "compaction",
4887
+ droppedTurns: compaction.droppedTurns,
4888
+ keptTurns: compaction.keptTurns,
4889
+ beforeTokens: compaction.beforeTokens,
4890
+ afterTokens: compaction.afterTokens,
4891
+ strategy: compaction.strategy
4892
+ };
4893
+ }
4894
+ }
3826
4895
  const startTime = Date.now();
3827
4896
  let toolRounds = 0;
3828
4897
  const toolExecutor = this.#buildToolExecutor();
@@ -4077,6 +5146,79 @@ ${item.result.diff.patch}`;
4077
5146
  this.#checkpoints.clear();
4078
5147
  this.#lastRoundResults = null;
4079
5148
  }
5149
+ // -------------------------------------------------------------------------
5150
+ // 上下文压缩(Compactor) — P0-1 实施
5151
+ // -------------------------------------------------------------------------
5152
+ /**
5153
+ * 获取当前上下文的统计信息(消息数、估算 token、占窗口比例)。
5154
+ * 用于 UI 状态栏展示和自动压缩阈值判断。
5155
+ *
5156
+ * @returns ContextStats;contextWindow 取自当前模型的 meta.contextWindow
5157
+ *
5158
+ * @pure 不修改任何状态
5159
+ */
5160
+ getContextStats() {
5161
+ const meta = getModelMeta(this.#provider.model());
5162
+ return getContextStats(this.#messages, meta.contextWindow);
5163
+ }
5164
+ /**
5165
+ * 手动触发上下文压缩。
5166
+ * 与自动压缩逻辑复用同一个 compactContext();若回合数过少则返回空结果(droppedTurns=0)。
5167
+ * 可被 UI 的 /compact 命令调用。
5168
+ *
5169
+ * @param onProgress — 可选进度回调(UI 实时展示压缩进度)
5170
+ * @returns 压缩结果;droppedTurns=0 表示无实际压缩发生
5171
+ *
5172
+ * @sideEffect 可能调一次 provider.chat()(LLM 摘要);成功后改写 #messages
5173
+ */
5174
+ async compact(onProgress) {
5175
+ const meta = getModelMeta(this.#provider.model());
5176
+ const result = await compactContext(this.#messages, {
5177
+ contextWindow: meta.contextWindow,
5178
+ autoCompactRatio: this.#options.autoCompactRatio,
5179
+ preserveRecentRounds: this.#options.preserveRecentRoundsOnCompact,
5180
+ minTurnsToCompact: this.#options.minTurnsToCompact,
5181
+ provider: this.#provider,
5182
+ signal: this.#abortController.signal,
5183
+ ...onProgress ? { onProgress } : {}
5184
+ });
5185
+ if (result.droppedTurns > 0) {
5186
+ this.#messages.length = 0;
5187
+ for (const m of result.messages) this.#messages.push(m);
5188
+ this.#checkpoints.clear();
5189
+ this.#persist();
5190
+ }
5191
+ return result;
5192
+ }
5193
+ /**
5194
+ * chat() 入口调用的自动压缩检查。
5195
+ * 仅在 enableAutoCompact=true 且应自动压缩时执行;否则返回 null。
5196
+ * 策略与 compact() 相同;返回值额外补上 strategy("summary" | "fallback")供 chat 事件使用。
5197
+ *
5198
+ * @returns 压缩结果;未压缩时返回 null
5199
+ *
5200
+ * @sideEffect 同 compact()
5201
+ */
5202
+ async #maybeAutoCompact() {
5203
+ const meta = getModelMeta(this.#provider.model());
5204
+ const opts = {
5205
+ contextWindow: meta.contextWindow,
5206
+ autoCompactRatio: this.#options.autoCompactRatio,
5207
+ preserveRecentRounds: this.#options.preserveRecentRoundsOnCompact,
5208
+ minTurnsToCompact: this.#options.minTurnsToCompact,
5209
+ provider: this.#provider,
5210
+ signal: this.#abortController.signal
5211
+ };
5212
+ if (!shouldAutoCompact(this.#messages, opts)) return null;
5213
+ const result = await compactContext(this.#messages, opts);
5214
+ if (result.droppedTurns === 0) return null;
5215
+ const strategy = result.summary.includes("\u672C\u5730\u6458\u8981") ? "fallback" : "summary";
5216
+ this.#messages.length = 0;
5217
+ for (const m of result.messages) this.#messages.push(m);
5218
+ this.#checkpoints.clear();
5219
+ this.#persist();
5220
+ return { ...result, strategy };
5221
+ }
4080
5222
  /**
4081
5223
  * 立即把当前状态写入持久化(不等 500ms debounce)。
4082
5224
  * 用于 UI 上"立即保存"按钮或异常退出前的最后兜底。
@@ -5913,9 +7055,9 @@ function joinReasoningSegments(segments) {
5913
7055
  }
5914
7056
 
5915
7057
  // src/ui/AnimatedLogo.tsx
5916
- import { Box as Box10, Text as Text11 } from "ink";
7058
+ import { Box as Box11, Text as Text12 } from "ink";
5917
7059
  import { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
5918
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
7060
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
5919
7061
  var LOGO_LINES2 = [
5920
7062
  " \u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584 \u2584\u2584",
5921
7063
  " \u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\u2584\u2580\u2588\u2588\u2588\u2580\u2580",
@@ -5944,14 +7086,14 @@ function AnimatedLogo({ panelWidth }) {
5944
7086
  const leftClip = Math.max(0, -pos);
5945
7087
  const rightClip = Math.max(0, pos + LOGO_WIDTH - panelWidth);
5946
7088
  const padding = Math.max(0, pos);
5947
- return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", alignItems: "flex-start", children: LOGO_LINES2.map((line, i) => /* @__PURE__ */ jsxs10(Text11, { color: LOGO_COLOR, bold: true, children: [
7089
+ return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", alignItems: "flex-start", children: LOGO_LINES2.map((line, i) => /* @__PURE__ */ jsxs11(Text12, { color: LOGO_COLOR, bold: true, children: [
5948
7090
  " ".repeat(padding),
5949
7091
  line.slice(leftClip, LOGO_WIDTH - rightClip)
5950
7092
  ] }, i)) });
5951
7093
  }
5952
7094
 
5953
7095
  // src/ui/ChatSession.tsx
5954
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
7096
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
5955
7097
  var PHASE_CONFIG = {
5956
7098
  thinking: { icon: "\u{1F9E0}", label: "\u601D\u8003\u4E2D", color: "#ff9800" },
5957
7099
  generating: { icon: "\u2728", label: "\u751F\u6210\u4E2D", color: "#00ff41" },
@@ -6026,6 +7168,13 @@ registerCommand("/rewind", {
6026
7168
  content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /rewind \u67E5\u770B\u53EF\u56DE\u9000\u7684\u68C0\u67E5\u70B9\u5217\u8868\uFF0C\u6216 /rewind <\u5E8F\u53F7> \u76F4\u63A5\u56DE\u9000\uFF081 = \u6700\u65B0\uFF0C2 = \u4E0A\u4E00\u6B21\uFF0C\u4F9D\u6B64\u7C7B\u63A8\uFF09"
6027
7169
  })
6028
7170
  });
7171
+ registerCommand("/compact", {
7172
+ desc: "\u538B\u7F29\u4E0A\u4E0B\u6587\uFF1A\u6458\u8981\u65E7\u56DE\u5408\u5E76\u4FDD\u7559\u6700\u8FD1 N \u8F6E",
7173
+ handler: () => ({
7174
+ kind: "text",
7175
+ content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /compact \u624B\u52A8\u538B\u7F29\u4E0A\u4E0B\u6587\uFF08\u9700\u5F53\u524D\u4E0D\u5904\u4E8E\u751F\u6210\u4E2D\uFF09"
7176
+ })
7177
+ });
6029
7178
  var STREAMING_PLACEHOLDERS = [
6030
7179
  "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
6031
7180
  "\u9A6C\u4E0A\u5C31\u597D...",
@@ -6069,6 +7218,7 @@ function ChatSession({
6069
7218
  const [balance, setBalance] = useState5(null);
6070
7219
  const [balanceLoading, setBalanceLoading] = useState5(false);
6071
7220
  const [todayCost, setTodayCost] = useState5(null);
7221
+ const [contextStats, setContextStats] = useState5(null);
6072
7222
  const [isStreaming, setIsStreaming] = useState5(false);
6073
7223
  const [streamingPhase, setStreamingPhase] = useState5(null);
6074
7224
  const [streamingPlaceholder, setStreamingPlaceholder] = useState5("");
@@ -6099,7 +7249,7 @@ function ChatSession({
6099
7249
  const [toolChoice, setToolChoice] = useState5(
6100
7250
  void 0
6101
7251
  );
6102
- const sessionCost = useMemo(() => {
7252
+ const sessionCost = useMemo2(() => {
6103
7253
  return displayMessages.reduce((sum, msg) => {
6104
7254
  if (msg.assistantDetail?.cost) {
6105
7255
  return sum + msg.assistantDetail.cost;
@@ -6128,6 +7278,12 @@ function ChatSession({
6128
7278
  const [selectingModel, setSelectingModel] = useState5(false);
6129
7279
  const [modelSelectIndex, setModelSelectIndex] = useState5(0);
6130
7280
  const modelOptions = ["deepseek-v4-flash", "deepseek-v4-pro"];
7281
+ const [compactionState, setCompactionState] = useState5({
7282
+ phase: "idle",
7283
+ progress: null
7284
+ });
7285
+ const compactionSettleTimerRef = useRef4(null);
7286
+ const compactionStrategyRef = useRef4(void 0);
6131
7287
  const sessionRef = useRef4(null);
6132
7288
  const abortRef = useRef4(null);
6133
7289
  const currentContentRef = useRef4("");
@@ -6148,7 +7304,33 @@ function ChatSession({
6148
7304
  clearTimeout(rewindHintTimerRef.current);
6149
7305
  rewindHintTimerRef.current = null;
6150
7306
  }
7307
+ if (compactionSettleTimerRef.current) {
7308
+ clearTimeout(compactionSettleTimerRef.current);
7309
+ compactionSettleTimerRef.current = null;
7310
+ }
7311
+ };
7312
+ }, []);
7313
+ useEffect5(() => {
7314
+ const refresh = () => {
7315
+ const session = sessionRef.current;
7316
+ if (!session) {
7317
+ setContextStats(null);
7318
+ return;
7319
+ }
7320
+ try {
7321
+ const s = session.getContextStats();
7322
+ setContextStats({
7323
+ tokens: s.estimatedTokens,
7324
+ ratio: s.ratio,
7325
+ contextWindow: s.contextWindow
7326
+ });
7327
+ } catch {
7328
+ setContextStats(null);
7329
+ }
6151
7330
  };
7331
+ refresh();
7332
+ const id = setInterval(refresh, 3e3);
7333
+ return () => clearInterval(id);
6152
7334
  }, []);
6153
7335
  useEffect5(() => {
6154
7336
  if (todoHideTimerRef.current) {
@@ -6641,6 +7823,146 @@ function ChatSession({
6641
7823
  setInput("");
6642
7824
  return;
6643
7825
  }
7826
+ if (cmdLower === "/compact") {
7827
+ if (isStreaming) {
7828
+ setDisplayMessages((prev) => [
7829
+ ...prev,
7830
+ { role: "user", content: trimmed },
7831
+ { role: "assistant", content: "\u26A0 \u6B63\u5728\u751F\u6210\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /compact\u3002" }
7832
+ ]);
7833
+ setInput("");
7834
+ return;
7835
+ }
7836
+ if (!sessionRef.current) {
7837
+ setDisplayMessages((prev) => [
7838
+ ...prev,
7839
+ { role: "user", content: trimmed },
7840
+ { role: "assistant", content: "\u26A0 Session \u672A\u5C31\u7EEA\uFF0C\u65E0\u6CD5\u538B\u7F29\u3002" }
7841
+ ]);
7842
+ setInput("");
7843
+ return;
7844
+ }
7845
+ setInput("");
7846
+ setDisplayMessages((prev) => [...prev, { role: "user", content: trimmed }]);
7847
+ setCompactionState({
7848
+ phase: "running",
7849
+ progress: null,
7850
+ beforeTokens: void 0,
7851
+ afterTokens: void 0,
7852
+ droppedTurns: void 0,
7853
+ keptTurns: void 0,
7854
+ strategy: void 0,
7855
+ errorMessage: void 0
7856
+ });
7857
+ try {
7858
+ const result = await sessionRef.current.compact(
7859
+ (event) => {
7860
+ setCompactionState((prev) => {
7861
+ if (event.type === "start") {
7862
+ return {
7863
+ ...prev,
7864
+ phase: "running",
7865
+ progress: event,
7866
+ beforeTokens: event.beforeTokens,
7867
+ droppedTurns: event.droppedTurns
7868
+ };
7869
+ }
7870
+ if (event.type === "summary_delta") {
7871
+ return {
7872
+ ...prev,
7873
+ phase: "running",
7874
+ progress: event
7875
+ };
7876
+ }
7877
+ if (event.type === "fallback") {
7878
+ compactionStrategyRef.current = "fallback";
7879
+ return {
7880
+ ...prev,
7881
+ phase: "running",
7882
+ progress: event,
7883
+ strategy: "fallback"
7884
+ };
7885
+ }
7886
+ if (event.type === "done") {
7887
+ const finalStrategy = compactionStrategyRef.current === "fallback" ? "fallback" : "summary";
7888
+ compactionStrategyRef.current = finalStrategy;
7889
+ return {
7890
+ ...prev,
7891
+ phase: "done",
7892
+ progress: event,
7893
+ droppedTurns: event.droppedTurns,
7894
+ keptTurns: event.keptTurns,
7895
+ beforeTokens: event.beforeTokens,
7896
+ afterTokens: event.afterTokens,
7897
+ strategy: finalStrategy
7898
+ };
7899
+ }
7900
+ return prev;
7901
+ });
7902
+ }
7903
+ );
7904
+ if (result.droppedTurns === 0) {
7905
+ setCompactionState((prev) => ({
7906
+ ...prev,
7907
+ phase: "done",
7908
+ progress: prev.progress,
7909
+ droppedTurns: 0,
7910
+ keptTurns: result.keptTurns,
7911
+ beforeTokens: result.beforeTokens,
7912
+ afterTokens: result.afterTokens,
7913
+ strategy: void 0
7914
+ }));
7915
+ if (compactionSettleTimerRef.current) {
7916
+ clearTimeout(compactionSettleTimerRef.current);
7917
+ }
7918
+ compactionSettleTimerRef.current = setTimeout(() => {
7919
+ setCompactionState({ phase: "idle", progress: null });
7920
+ setDisplayMessages((prev) => [
7921
+ ...prev,
7922
+ {
7923
+ role: "assistant",
7924
+ content: "\u2139 \u5F53\u524D\u5BF9\u8BDD\u592A\u77ED\uFF0C\u65E0\u9700\u538B\u7F29\uFF08\u5C11\u4E8E\u6700\u5C0F\u56DE\u5408\u6570 \u6216 \u90FD\u5728\u4FDD\u7559\u533A\u5185\uFF09\u3002"
7925
+ }
7926
+ ]);
7927
+ }, 2500);
7928
+ } else {
7929
+ if (compactionSettleTimerRef.current) {
7930
+ clearTimeout(compactionSettleTimerRef.current);
7931
+ }
7932
+ compactionSettleTimerRef.current = setTimeout(() => {
7933
+ const ratioSaved = ((result.beforeTokens - result.afterTokens) / Math.max(result.beforeTokens, 1) * 100).toFixed(1);
7934
+ const strategyNote = compactionStrategyRef.current === "fallback" ? "\n\u26A0 \u6458\u8981\u4E3A\u672C\u5730\u5151\u5E95\uFF08LLM \u4E0D\u53EF\u7528\uFF09" : "";
7935
+ setCompactionState({ phase: "idle", progress: null });
7936
+ compactionStrategyRef.current = void 0;
7937
+ setDisplayMessages((prev) => [
7938
+ ...prev,
7939
+ {
7940
+ role: "assistant",
7941
+ content: `\u2714 \u538B\u7F29\u5B8C\u6210\uFF1A${result.droppedTurns} \u56DE\u5408 \u2192 ${result.keptTurns} \u56DE\u5408\uFF08\u8282\u7701 ${ratioSaved}% token\uFF09${strategyNote}`
7942
+ }
7943
+ ]);
7944
+ }, 4e3);
7945
+ }
7946
+ } catch (err) {
7947
+ const msg = err instanceof Error ? err.message : String(err);
7948
+ setCompactionState((prev) => ({
7949
+ ...prev,
7950
+ phase: "error",
7951
+ errorMessage: msg
7952
+ }));
7953
+ if (compactionSettleTimerRef.current) {
7954
+ clearTimeout(compactionSettleTimerRef.current);
7955
+ }
7956
+ compactionSettleTimerRef.current = setTimeout(() => {
7957
+ setCompactionState({ phase: "idle", progress: null });
7958
+ setDisplayMessages((prev) => [
7959
+ ...prev,
7960
+ { role: "assistant", content: `\u26A0 \u538B\u7F29\u5931\u8D25\uFF1A${msg}` }
7961
+ ]);
7962
+ }, 3e3);
7963
+ }
7964
+ return;
7965
+ }
6644
7966
  if (cmdLower === "/model") {
6645
7967
  const curIdx = modelOptions.indexOf(activeModel);
6646
7968
  setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
@@ -6829,6 +8151,12 @@ function ChatSession({
6829
8151
  todoHideTimerRef.current = null;
6830
8152
  }
6831
8153
  setTodoPanelVisible(false);
8154
+ setCompactionState({ phase: "idle", progress: null });
8155
+ compactionStrategyRef.current = void 0;
8156
+ if (compactionSettleTimerRef.current) {
8157
+ clearTimeout(compactionSettleTimerRef.current);
8158
+ compactionSettleTimerRef.current = null;
8159
+ }
6832
8160
  const session = sessionRef.current;
6833
8161
  const abortController = new AbortController();
6834
8162
  abortRef.current = abortController;
@@ -7002,16 +8330,16 @@ function ChatSession({
7002
8330
  setTodayCost(externalCostTracker.todayTotalCost);
7003
8331
  }
7004
8332
  }, [isStreaming, externalCostTracker]);
7005
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
7006
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", flexGrow: 1, children: [
7007
- /* @__PURE__ */ jsx11(
7008
- Box11,
8333
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
8334
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", flexGrow: 1, children: [
8335
+ /* @__PURE__ */ jsx12(
8336
+ Box12,
7009
8337
  {
7010
8338
  width: leftPanelWidth,
7011
8339
  flexShrink: 0,
7012
8340
  flexDirection: "column",
7013
- children: /* @__PURE__ */ jsx11(
7014
- Box11,
8341
+ children: /* @__PURE__ */ jsx12(
8342
+ Box12,
7015
8343
  {
7016
8344
  paddingX: 1,
7017
8345
  flexDirection: "column",
@@ -7019,51 +8347,51 @@ function ChatSession({
7019
8347
  justifyContent: !hasConversationStarted ? "center" : "flex-end",
7020
8348
  children: !hasConversationStarted ? (
7021
8349
  /* ===== 首页:Logo + 状态概况 ===== */
7022
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", alignItems: "center", children: [
8350
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", alignItems: "center", children: [
7023
8351
  LOGO_LINES.map((line, i) => {
7024
8352
  const colorIndex = (i + offset) % CYBER_PALETTE.length;
7025
- return /* @__PURE__ */ jsx11(Text12, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }, i);
8353
+ return /* @__PURE__ */ jsx12(Text13, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }, i);
7026
8354
  }),
7027
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "#808080", children: [
8355
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "#808080", children: [
7028
8356
  "\u{1F4E6} v",
7029
8357
  VERSION
7030
8358
  ] }) }),
7031
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
7032
- /* @__PURE__ */ jsxs11(Text12, { color: "#00ff41", children: [
8359
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
8360
+ /* @__PURE__ */ jsxs12(Text13, { color: "#00ff41", children: [
7033
8361
  "\u2714 ",
7034
8362
  skillCount,
7035
8363
  " Skills"
7036
8364
  ] }),
7037
- /* @__PURE__ */ jsxs11(Text12, { color: "#00ffff", children: [
8365
+ /* @__PURE__ */ jsxs12(Text13, { color: "#00ffff", children: [
7038
8366
  "\u2139 ",
7039
8367
  toolCount,
7040
8368
  " \u5DE5\u5177"
7041
8369
  ] }),
7042
- /* @__PURE__ */ jsxs11(Text12, { color: "#00ffff", children: [
8370
+ /* @__PURE__ */ jsxs12(Text13, { color: "#00ffff", children: [
7043
8371
  "\u{1F527} ",
7044
8372
  SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
7045
8373
  ] }),
7046
- thinkingEnabled && /* @__PURE__ */ jsxs11(Text12, { color: "#ff9800", children: [
8374
+ thinkingEnabled && /* @__PURE__ */ jsxs12(Text13, { color: "#ff9800", children: [
7047
8375
  "\u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
7048
8376
  thinkingEffort === "max" ? "Max" : "High"
7049
8377
  ] }),
7050
- sessionMode === "plan" && /* @__PURE__ */ jsx11(Text12, { color: "#ff69b4", bold: true, children: "\u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
8378
+ sessionMode === "plan" && /* @__PURE__ */ jsx12(Text13, { color: "#ff69b4", bold: true, children: "\u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
7051
8379
  ] }),
7052
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
7053
- balance !== null ? /* @__PURE__ */ jsxs11(Text12, { color: "yellow", children: [
8380
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
8381
+ balance !== null ? /* @__PURE__ */ jsxs12(Text13, { color: "yellow", children: [
7054
8382
  "\u{1F4B0} \u4F59\u989D \xA5",
7055
8383
  balance.toFixed(2)
7056
- ] }) : balanceLoading ? /* @__PURE__ */ jsx11(Text12, { color: "yellow", children: "\u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : null,
7057
- todayCost !== null && /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
8384
+ ] }) : balanceLoading ? /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: "\u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : null,
8385
+ todayCost !== null && /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
7058
8386
  "\u{1F4CA} \u4ECA\u65E5 \xA5",
7059
8387
  todayCost.toFixed(2)
7060
8388
  ] })
7061
8389
  ] }),
7062
- verbose && /* @__PURE__ */ jsx11(Text12, { color: "#ff1493", children: "\u26A1 Verbose" })
8390
+ verbose && /* @__PURE__ */ jsx12(Text13, { color: "#ff1493", children: "\u26A1 Verbose" })
7063
8391
  ] })
7064
8392
  ) : hasReasoningPanel ? (
7065
8393
  /* ===== 流式思考中(纯文本,无边框) ===== */
7066
- /* @__PURE__ */ jsx11(Text12, { dimColor: true, wrap: "wrap", children: (() => {
8394
+ /* @__PURE__ */ jsx12(Text13, { dimColor: true, wrap: "wrap", children: (() => {
7067
8395
  const full = joinReasoningSegments(currentReasoning);
7068
8396
  const maxContentLines = 11;
7069
8397
  const lines = full.split("\n");
@@ -7075,30 +8403,31 @@ function ChatSession({
7075
8403
  })() })
7076
8404
  ) : (
7077
8405
  /* ===== 对话中(无思考):会话状态面板 ===== */
7078
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", alignItems: "center", children: [
7079
- /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", bold: true, children: "\u{1F4AC} \u5BF9\u8BDD\u8FDB\u884C\u4E2D" }),
7080
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
7081
- /* @__PURE__ */ jsxs11(Text12, { color: "#00ff41", children: [
8406
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", alignItems: "center", children: [
8407
+ /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", bold: true, children: "\u{1F4AC} \u5BF9\u8BDD\u8FDB\u884C\u4E2D" }),
8408
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
8409
+ /* @__PURE__ */ jsxs12(Text13, { color: "#00ff41", children: [
7082
8410
  "\u{1F4DD} \u6D88\u606F ",
7083
8411
  displayMessages.length,
7084
8412
  " \u6761"
7085
8413
  ] }),
7086
- sessionCost > 0 && /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
8414
+ sessionCost > 0 && /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
7087
8415
  "\u{1F4B0} \u4F1A\u8BDD \xA5",
7088
8416
  sessionCost.toFixed(4)
7089
- ] })
8417
+ ] }),
8418
+ contextStats && /* @__PURE__ */ jsx12(ContextUsageBar, { stats: contextStats })
7090
8419
  ] }),
7091
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
7092
- /* @__PURE__ */ jsxs11(Text12, { color: "#808080", children: [
8420
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", alignItems: "center", children: [
8421
+ /* @__PURE__ */ jsxs12(Text13, { color: "#808080", children: [
7093
8422
  "\u{1F527} ",
7094
8423
  SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
7095
8424
  ] }),
7096
- thinkingEnabled && /* @__PURE__ */ jsxs11(Text12, { color: "#ff9800", children: [
8425
+ thinkingEnabled && /* @__PURE__ */ jsxs12(Text13, { color: "#ff9800", children: [
7097
8426
  "\u{1F9E0} ",
7098
8427
  thinkingEffort === "max" ? "Max" : "High"
7099
8428
  ] })
7100
8429
  ] }),
7101
- balance !== null && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "yellow", children: [
8430
+ balance !== null && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "yellow", children: [
7102
8431
  "\u{1F4B0} \xA5",
7103
8432
  balance.toFixed(2)
7104
8433
  ] }) })
@@ -7108,47 +8437,46 @@ function ChatSession({
7108
8437
  )
7109
8438
  }
7110
8439
  ),
7111
- /* @__PURE__ */ jsx11(Box11, { width: 1, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { color: "#444", children: "\u2502" }) }),
7112
- useMemo(() => /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", flexGrow: 1, children: [
7113
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", flexGrow: 1, children: [
7114
- !hasConversationStarted && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", alignItems: "flex-start", flexGrow: 1, paddingBottom: 1, children: [
8440
+ useMemo2(() => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", flexGrow: 1, children: [
8441
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", flexGrow: 1, children: [
8442
+ !hasConversationStarted && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", alignItems: "flex-start", flexGrow: 1, paddingBottom: 1, children: [
7115
8443
  cmdTips.length > 0 && (() => {
7116
8444
  const tip = cmdTips[cmdTipIndex % cmdTips.length];
7117
8445
  if (!tip) return null;
7118
8446
  const text = `${tip.name} ${tip.desc}`;
7119
- return /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, alignSelf: "center", children: [
7120
- /* @__PURE__ */ jsx11(Text12, { color: "#808080", children: "\u{1F4A1} " }),
7121
- cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx11(Text12, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx11(Text12, { color: "#808080", children: text })
8447
+ return /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, alignSelf: "center", children: [
8448
+ /* @__PURE__ */ jsx12(Text13, { color: "#808080", children: "\u{1F4A1} " }),
8449
+ cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx12(Text13, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx12(Text13, { color: "#808080", children: text })
7122
8450
  ] });
7123
8451
  })(),
7124
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1 }),
7125
- /* @__PURE__ */ jsx11(AnimatedLogo, { panelWidth: rightContentWidth })
8452
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1 }),
8453
+ /* @__PURE__ */ jsx12(AnimatedLogo, { panelWidth: rightContentWidth })
7126
8454
  ] }),
7127
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
7128
- /* @__PURE__ */ jsx11(Static, { items: displayMessages, children: (msg, i) => {
8455
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
8456
+ /* @__PURE__ */ jsx12(Static, { items: displayMessages, children: (msg, i) => {
7129
8457
  if (msg.role === "user") {
7130
- return /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "row", children: [
7131
- /* @__PURE__ */ jsx11(Box11, { width: 1, backgroundColor: "#FF8C00", flexShrink: 0 }),
7132
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1, paddingLeft: 1, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
7133
- /* @__PURE__ */ jsx11(Box11, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#FF8C00", children: "\u{1F4AC}" }) }),
7134
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1, children: /* @__PURE__ */ jsx11(Text12, { wrap: "wrap", children: msg.content }) })
8458
+ return /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "row", children: [
8459
+ /* @__PURE__ */ jsx12(Box12, { width: 1, backgroundColor: "#FF8C00", flexShrink: 0 }),
8460
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1, paddingLeft: 1, children: /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
8461
+ /* @__PURE__ */ jsx12(Box12, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#FF8C00", children: "\u{1F4AC}" }) }),
8462
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1, children: /* @__PURE__ */ jsx12(Text13, { wrap: "wrap", children: msg.content }) })
7135
8463
  ] }) })
7136
8464
  ] }, i);
7137
8465
  }
7138
8466
  if (msg.role === "tool") {
7139
- return /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "row", children: [
7140
- /* @__PURE__ */ jsx11(Box11, { width: 1, backgroundColor: "#666666", flexShrink: 0 }),
7141
- /* @__PURE__ */ jsxs11(Box11, { flexGrow: 1, paddingLeft: 1, flexDirection: "column", children: [
7142
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
7143
- /* @__PURE__ */ jsx11(Box11, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u{1F527}" }) }),
7144
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, wrap: "wrap", children: msg.content }) })
8467
+ return /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "row", children: [
8468
+ /* @__PURE__ */ jsx12(Box12, { width: 1, backgroundColor: "#666666", flexShrink: 0 }),
8469
+ /* @__PURE__ */ jsxs12(Box12, { flexGrow: 1, paddingLeft: 1, flexDirection: "column", children: [
8470
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
8471
+ /* @__PURE__ */ jsx12(Box12, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u{1F527}" }) }),
8472
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, wrap: "wrap", children: msg.content }) })
7145
8473
  ] }),
7146
- msg.diff && /* @__PURE__ */ jsx11(DiffPreview, { diff: msg.diff })
8474
+ msg.diff && /* @__PURE__ */ jsx12(DiffPreview, { diff: msg.diff })
7147
8475
  ] })
7148
8476
  ] }, i);
7149
8477
  }
7150
8478
  const detail = msg.assistantDetail;
7151
- return /* @__PURE__ */ jsx11(
8479
+ return /* @__PURE__ */ jsx12(
7152
8480
  AssistantMessage,
7153
8481
  {
7154
8482
  content: msg.content,
@@ -7162,7 +8490,7 @@ function ChatSession({
7162
8490
  i
7163
8491
  );
7164
8492
  } }, staticKey),
7165
- isStreaming && /* @__PURE__ */ jsx11(
8493
+ isStreaming && /* @__PURE__ */ jsx12(
7166
8494
  AssistantMessage,
7167
8495
  {
7168
8496
  content: currentContent,
@@ -7173,49 +8501,56 @@ function ChatSession({
7173
8501
  model: _streamingModel
7174
8502
  }
7175
8503
  ),
7176
- !isStreaming && streamError && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
8504
+ compactionState.phase !== "idle" && /* @__PURE__ */ jsx12(
8505
+ CompactionProgress,
8506
+ {
8507
+ state: compactionState,
8508
+ contentWidth: rightContentWidth
8509
+ }
8510
+ ),
8511
+ !isStreaming && streamError && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs12(Text13, { color: "red", children: [
7177
8512
  "\u26A0 ",
7178
8513
  streamError
7179
8514
  ] }) })
7180
8515
  ] }),
7181
- todoPanelVisible && todoSnapshot.length > 0 && /* @__PURE__ */ jsx11(TodoListPanel, { items: todoSnapshot })
8516
+ todoPanelVisible && todoSnapshot.length > 0 && /* @__PURE__ */ jsx12(TodoListPanel, { items: todoSnapshot })
7182
8517
  ] }),
7183
- selectingModel ? /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", children: [
7184
- /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }),
7185
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
7186
- /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
8518
+ selectingModel ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
8519
+ /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }),
8520
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
8521
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
7187
8522
  modelOptions.map((id, i) => {
7188
8523
  const meta = SUPPORTED_MODELS[id];
7189
8524
  const isCurrent = id === activeModel;
7190
8525
  const isSelected = i === modelSelectIndex;
7191
8526
  const marker = isSelected ? " > " : " ";
7192
8527
  const suffix = isCurrent ? " (\u5F53\u524D)" : "";
7193
- return /* @__PURE__ */ jsxs11(Box11, { children: [
7194
- /* @__PURE__ */ jsxs11(Text12, { color: isSelected ? "#00ff41" : void 0, bold: isSelected, children: [
8528
+ return /* @__PURE__ */ jsxs12(Box12, { children: [
8529
+ /* @__PURE__ */ jsxs12(Text13, { color: isSelected ? "#00ff41" : void 0, bold: isSelected, children: [
7195
8530
  marker,
7196
8531
  meta.displayName,
7197
8532
  suffix
7198
8533
  ] }),
7199
- isSelected && /* @__PURE__ */ jsxs11(Text12, { color: "#808080", children: [
8534
+ isSelected && /* @__PURE__ */ jsxs12(Text13, { color: "#808080", children: [
7200
8535
  " \u2014 ",
7201
8536
  id
7202
8537
  ] })
7203
8538
  ] }, id);
7204
8539
  }),
7205
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
8540
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
7206
8541
  ] }),
7207
- /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) })
7208
- ] }) : rewindSelecting ? /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", children: [
7209
- /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }),
7210
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
7211
- /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#ff9800", children: "\u9009\u62E9\u8981\u56DE\u9000\u7684\u68C0\u67E5\u70B9\uFF1A" }),
8542
+ /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) })
8543
+ ] }) : rewindSelecting ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
8544
+ /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }),
8545
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
8546
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#ff9800", children: "\u9009\u62E9\u8981\u56DE\u9000\u7684\u68C0\u67E5\u70B9\uFF1A" }),
7212
8547
  rewindList.map((cp2, i) => {
7213
8548
  const isSelected = i === rewindSelectIndex;
7214
8549
  const marker = isSelected ? " > " : " ";
7215
8550
  const time = new Date(cp2.timestamp).toLocaleTimeString();
7216
8551
  const preview = cp2.preview || "(\u7A7A)";
7217
8552
  const tag = cp2.isGitRepo ? "" : " [\u975E git\uFF0C\u4EC5\u5BF9\u8BDD]";
7218
- return /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: isSelected ? "#ff9800" : void 0, bold: isSelected, children: [
8553
+ return /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: isSelected ? "#ff9800" : void 0, bold: isSelected, children: [
7219
8554
  marker,
7220
8555
  "#",
7221
8556
  i + 1,
@@ -7227,21 +8562,25 @@ function ChatSession({
7227
8562
  "`"
7228
8563
  ] }) }, cp2.index);
7229
8564
  }),
7230
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
8565
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
7231
8566
  ] }),
7232
- /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) })
7233
- ] }) : /* @__PURE__ */ jsxs11(Fragment2, { children: [
7234
- (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx11(Box11, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs11(Text12, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
8567
+ /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) })
8568
+ ] }) : /* @__PURE__ */ jsxs12(Fragment3, { children: [
8569
+ (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx12(Box12, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs12(Text13, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
7235
8570
  PHASE_CONFIG[streamingPhase].icon,
7236
8571
  " ",
7237
8572
  PHASE_CONFIG[streamingPhase].label,
7238
8573
  " ",
7239
- /* @__PURE__ */ jsx11(InkSpinner3, { type: "dots" })
7240
- ] }) }) : rewindHintPhase === "visible" ? /* @__PURE__ */ jsx11(Box11, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx11(Text12, { color: "#808080", children: "\u21A9 /rewind 1 \u53EF\u64A4\u56DE\u672C\u6B21\u4FEE\u6539" }) }) : null,
7241
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }) }),
7242
- /* @__PURE__ */ jsxs11(Box11, { children: [
7243
- /* @__PURE__ */ jsx11(Box11, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
7244
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx11(Text12, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx11(Text12, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx11(Text12, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx11(Text12, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx11(
8574
+ /* @__PURE__ */ jsx12(InkSpinner4, { type: "dots" })
8575
+ ] }) }) : rewindHintPhase === "visible" ? /* @__PURE__ */ jsx12(Box12, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx12(Text13, { color: "#808080", children: "\u21A9 /rewind 1 \u53EF\u64A4\u56DE\u672C\u6B21\u4FEE\u6539" }) }) : contextStats && contextStats.ratio > 0.85 ? /* @__PURE__ */ jsx12(Box12, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs12(Text13, { color: "#ff9800", children: [
8576
+ "\u{1F4DA} \u4E0A\u4E0B\u6587\u5DF2\u7528 ",
8577
+ (contextStats.ratio * 100).toFixed(0),
8578
+ "%\uFF0C\u8F93\u5165 /compact \u53EF\u624B\u52A8\u538B\u7F29"
8579
+ ] }) }) : null,
8580
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }) }),
8581
+ /* @__PURE__ */ jsxs12(Box12, { children: [
8582
+ /* @__PURE__ */ jsx12(Box12, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
8583
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx12(Text13, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx12(Text13, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx12(Text13, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx12(Text13, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx12(
7245
8584
  TextInput,
7246
8585
  {
7247
8586
  value: input,
@@ -7252,9 +8591,9 @@ function ChatSession({
7252
8591
  inputKey
7253
8592
  ) })
7254
8593
  ] }),
7255
- /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Text12, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }) }),
7256
- /* @__PURE__ */ jsx11(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
7257
- /* @__PURE__ */ jsx11(FileSelector, { files, input, selectedIndex: fileSelectIndex })
8594
+ /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(rightContentWidth) }) }),
8595
+ /* @__PURE__ */ jsx12(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
8596
+ /* @__PURE__ */ jsx12(FileSelector, { files, input, selectedIndex: fileSelectIndex })
7258
8597
  ] })
7259
8598
  ] }), [
7260
8599
  displayMessages,
@@ -7302,15 +8641,53 @@ function ChatSession({
7302
8641
  doubleCtrlC
7303
8642
  ])
7304
8643
  ] }),
7305
- doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
7306
- isStreaming && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
8644
+ doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
8645
+ isStreaming && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
8646
+ ] });
8647
+ }
8648
+ var CONTEXT_BAR_WIDTH = 12;
8649
+ function classifyContextRatio(ratio) {
8650
+ if (ratio >= 0.85) return "danger";
8651
+ if (ratio >= 0.6) return "warning";
8652
+ return "safe";
8653
+ }
8654
+ function contextLevelColor(level) {
8655
+ switch (level) {
8656
+ case "danger":
8657
+ return "#ff6347";
8658
+ // 红色
8659
+ case "warning":
8660
+ return "#ffcc00";
8661
+ // 黄色
8662
+ case "safe":
8663
+ return "#00ff41";
8664
+ }
8665
+ }
8666
+ function ContextUsageBar({
8667
+ stats
8668
+ }) {
8669
+ const level = classifyContextRatio(stats.ratio);
8670
+ const color = contextLevelColor(level);
8671
+ const filled = Math.min(
8672
+ CONTEXT_BAR_WIDTH,
8673
+ // 只要 ratio > 0 就至少 1 格 █,避免“占用极少”时空条被误读为“未使用”
8674
+ Math.max(stats.ratio > 0 ? 1 : 0, Math.round(stats.ratio * CONTEXT_BAR_WIDTH))
8675
+ );
8676
+ const empty = CONTEXT_BAR_WIDTH - filled;
8677
+ return /* @__PURE__ */ jsxs12(Text13, { color, children: [
8678
+ "\u{1F4DA} ",
8679
+ /* @__PURE__ */ jsx12(Text13, { color, children: "\u2588".repeat(filled) }),
8680
+ /* @__PURE__ */ jsx12(Text13, { color: "#444444", children: "\u2591".repeat(empty) }),
8681
+ " ",
8682
+ (stats.ratio * 100).toFixed(1),
8683
+ "%"
7307
8684
  ] });
7308
8685
  }
7309
8686
 
7310
8687
  // src/ui/GamePicker.tsx
7311
- import { Box as Box12, Text as Text13, useInput as useInput2 } from "ink";
8688
+ import { Box as Box13, Text as Text14, useInput as useInput2 } from "ink";
7312
8689
  import { useState as useState6, useCallback as useCallback3 } from "react";
7313
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
8690
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
7314
8691
  function GamePicker({ games, onSelect, onExit, onBackToChat }) {
7315
8692
  const [selectedIndex, setSelectedIndex] = useState6(0);
7316
8693
  const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
@@ -7338,18 +8715,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
7338
8715
  [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
7339
8716
  )
7340
8717
  );
7341
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7342
- /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
7343
- /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: games.map((game, index) => {
8718
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
8719
+ /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text14, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
8720
+ /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", children: games.map((game, index) => {
7344
8721
  const isSelected = index === selectedIndex;
7345
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
7346
- /* @__PURE__ */ jsx12(Box12, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx12(Text13, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx12(Text13, { children: " " }) }),
7347
- /* @__PURE__ */ jsx12(Box12, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
7348
- /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { color: "#888888", children: game.description }) })
8722
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", children: [
8723
+ /* @__PURE__ */ jsx13(Box13, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx13(Text14, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx13(Text14, { children: " " }) }),
8724
+ /* @__PURE__ */ jsx13(Box13, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text14, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
8725
+ /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text14, { color: "#888888", children: game.description }) })
7349
8726
  ] }, game.id);
7350
8727
  }) }),
7351
- /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
7352
- doubleCtrlC && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
8728
+ /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
8729
+ doubleCtrlC && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
7353
8730
  ] });
7354
8731
  }
7355
8732
 
@@ -7366,9 +8743,9 @@ function listGames() {
7366
8743
  }
7367
8744
 
7368
8745
  // src/game/brick-breaker/index.tsx
7369
- import { Box as Box13, Text as Text14, useInput as useInput3, render as render2 } from "ink";
8746
+ import { Box as Box14, Text as Text15, useInput as useInput3, render as render2 } from "ink";
7370
8747
  import { useState as useState7, useEffect as useEffect6, useRef as useRef5, useCallback as useCallback4 } from "react";
7371
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
8748
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
7372
8749
  var GAME_WIDTH = 40;
7373
8750
  var GAME_HEIGHT = 18;
7374
8751
  var PADDLE_WIDTH = 9;
@@ -7558,49 +8935,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
7558
8935
  const board = buildBoard(s);
7559
8936
  const def = getLevel(s.level);
7560
8937
  void tick2;
7561
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
7562
- /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", children: [
7563
- /* @__PURE__ */ jsx13(Box13, { width: 20, children: /* @__PURE__ */ jsxs13(Text14, { children: [
8938
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
8939
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", children: [
8940
+ /* @__PURE__ */ jsx14(Box14, { width: 20, children: /* @__PURE__ */ jsxs14(Text15, { children: [
7564
8941
  "\u5173\u5361 ",
7565
8942
  s.level,
7566
8943
  ": ",
7567
- /* @__PURE__ */ jsx13(Text14, { color: "cyan", children: def.desc })
8944
+ /* @__PURE__ */ jsx14(Text15, { color: "cyan", children: def.desc })
7568
8945
  ] }) }),
7569
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { children: [
8946
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { children: [
7570
8947
  "\u5206\u6570: ",
7571
- /* @__PURE__ */ jsx13(Text14, { color: "yellow", children: String(s.score).padStart(3, "0") })
8948
+ /* @__PURE__ */ jsx14(Text15, { color: "yellow", children: String(s.score).padStart(3, "0") })
7572
8949
  ] }) }),
7573
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { children: [
8950
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { children: [
7574
8951
  "\u751F\u547D: ",
7575
- /* @__PURE__ */ jsx13(Text14, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
8952
+ /* @__PURE__ */ jsx14(Text15, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
7576
8953
  ] }) }),
7577
- /* @__PURE__ */ jsx13(Box13, { width: 10, children: /* @__PURE__ */ jsxs13(Text14, { children: [
8954
+ /* @__PURE__ */ jsx14(Box14, { width: 10, children: /* @__PURE__ */ jsxs14(Text15, { children: [
7578
8955
  "\u7816\u5757: ",
7579
- /* @__PURE__ */ jsx13(Text14, { color: "cyan", children: aliveCount })
8956
+ /* @__PURE__ */ jsx14(Text15, { color: "cyan", children: aliveCount })
7580
8957
  ] }) }),
7581
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsxs13(Text14, { color: s.paused ? "gray" : "green", children: [
8958
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: s.paused ? "gray" : "green", children: [
7582
8959
  "[",
7583
8960
  s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
7584
8961
  "]"
7585
8962
  ] }) })
7586
8963
  ] }),
7587
- /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
7588
- /* @__PURE__ */ jsxs13(Text14, { children: [
8964
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
8965
+ /* @__PURE__ */ jsxs14(Text15, { children: [
7589
8966
  "\u250C",
7590
8967
  "\u2500".repeat(GAME_WIDTH),
7591
8968
  "\u2510"
7592
8969
  ] }),
7593
- /* @__PURE__ */ jsx13(Text14, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
7594
- /* @__PURE__ */ jsxs13(Text14, { children: [
8970
+ /* @__PURE__ */ jsx14(Text15, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
8971
+ /* @__PURE__ */ jsxs14(Text15, { children: [
7595
8972
  "\u2514",
7596
8973
  "\u2500".repeat(GAME_WIDTH),
7597
8974
  "\u2518"
7598
8975
  ] })
7599
8976
  ] }),
7600
- selectingLevel && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
7601
- /* @__PURE__ */ jsx13(Text14, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
7602
- /* @__PURE__ */ jsx13(Box13, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx13(Box13, { width: 22, children: /* @__PURE__ */ jsxs13(Text14, { children: [
7603
- /* @__PURE__ */ jsx13(Text14, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
8977
+ selectingLevel && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
8978
+ /* @__PURE__ */ jsx14(Text15, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
8979
+ /* @__PURE__ */ jsx14(Box14, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx14(Box14, { width: 22, children: /* @__PURE__ */ jsxs14(Text15, { children: [
8980
+ /* @__PURE__ */ jsx14(Text15, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
7604
8981
  ". ",
7605
8982
  lv.desc,
7606
8983
  " (",
@@ -7609,16 +8986,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
7609
8986
  lv.cols,
7610
8987
  ")"
7611
8988
  ] }) }, i)) }),
7612
- /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
8989
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
7613
8990
  ] }),
7614
- !selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
7615
- /* @__PURE__ */ jsx13(Text14, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
7616
- /* @__PURE__ */ jsxs13(Text14, { children: [
8991
+ !selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
8992
+ /* @__PURE__ */ jsx14(Text15, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
8993
+ /* @__PURE__ */ jsxs14(Text15, { children: [
7617
8994
  " \u5206\u6570: ",
7618
- /* @__PURE__ */ jsx13(Text14, { color: "yellow", children: s.score })
8995
+ /* @__PURE__ */ jsx14(Text15, { color: "yellow", children: s.score })
7619
8996
  ] })
7620
8997
  ] }),
7621
- !selectingLevel && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
8998
+ !selectingLevel && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
7622
8999
  ] });
7623
9000
  }
7624
9001
  var brick_breaker_default = {
@@ -7628,7 +9005,7 @@ var brick_breaker_default = {
7628
9005
  play: async () => {
7629
9006
  await new Promise((resolve2) => {
7630
9007
  const { unmount } = render2(
7631
- /* @__PURE__ */ jsx13(BrickBreakerGame, { onExit: () => {
9008
+ /* @__PURE__ */ jsx14(BrickBreakerGame, { onExit: () => {
7632
9009
  unmount();
7633
9010
  resolve2();
7634
9011
  } })
@@ -7638,9 +9015,9 @@ var brick_breaker_default = {
7638
9015
  };
7639
9016
 
7640
9017
  // src/game/coder-check/index.tsx
7641
- import { Box as Box14, Text as Text15, useInput as useInput4, render as render3 } from "ink";
9018
+ import { Box as Box15, Text as Text16, useInput as useInput4, render as render3 } from "ink";
7642
9019
  import { useState as useState8, useEffect as useEffect7, useRef as useRef6, useCallback as useCallback5 } from "react";
7643
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
9020
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
7644
9021
  var GAME_W = 66;
7645
9022
  var GAME_H = 20;
7646
9023
  var SCORE_H = 6;
@@ -8111,44 +9488,44 @@ function CoderCheck({ onExit: _onExit }) {
8111
9488
  const scoreLines = buildScoreLines(scoreStr);
8112
9489
  const view = buildGameView(s, scoreLines, scoreColor, s.message);
8113
9490
  void tick2;
8114
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingX: 1, children: [
8115
- /* @__PURE__ */ jsx14(Box14, { flexDirection: "row", children: /* @__PURE__ */ jsxs14(Text15, { children: [
9491
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, children: [
9492
+ /* @__PURE__ */ jsx15(Box15, { flexDirection: "row", children: /* @__PURE__ */ jsxs15(Text16, { children: [
8116
9493
  "\u751F\u547D ",
8117
- /* @__PURE__ */ jsx14(Text15, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
9494
+ /* @__PURE__ */ jsx15(Text16, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
8118
9495
  " ",
8119
9496
  "\u901F\u5EA6 ",
8120
- /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
9497
+ /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
8121
9498
  "Lv.",
8122
9499
  Math.floor(s.speed * 10)
8123
9500
  ] })
8124
9501
  ] }) }),
8125
- !s.gameOver && s.target && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { children: [
9502
+ !s.gameOver && s.target && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { children: [
8126
9503
  "\u6253\u5B57: ",
8127
- /* @__PURE__ */ jsx14(Text15, { color: "green", children: s.typed }),
8128
- /* @__PURE__ */ jsx14(Text15, { color: "white", children: s.target.slice(s.typed.length) })
9504
+ /* @__PURE__ */ jsx15(Text16, { color: "green", children: s.typed }),
9505
+ /* @__PURE__ */ jsx15(Text16, { color: "white", children: s.target.slice(s.typed.length) })
8129
9506
  ] }) }),
8130
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
8131
- /* @__PURE__ */ jsxs14(Text15, { children: [
9507
+ /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginTop: 1, children: [
9508
+ /* @__PURE__ */ jsxs15(Text16, { children: [
8132
9509
  "\u250C",
8133
9510
  "\u2500".repeat(GAME_W),
8134
9511
  "\u2510"
8135
9512
  ] }),
8136
- view.map((row, i) => /* @__PURE__ */ jsx14(Text15, { children: `\u2502${row}\u2502` }, i)),
8137
- /* @__PURE__ */ jsxs14(Text15, { children: [
9513
+ view.map((row, i) => /* @__PURE__ */ jsx15(Text16, { children: `\u2502${row}\u2502` }, i)),
9514
+ /* @__PURE__ */ jsxs15(Text16, { children: [
8138
9515
  "\u2514",
8139
9516
  "\u2500".repeat(GAME_W),
8140
9517
  "\u2518"
8141
9518
  ] })
8142
9519
  ] }),
8143
- s.gameOver && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
8144
- /* @__PURE__ */ jsx14(Text15, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
8145
- /* @__PURE__ */ jsxs14(Text15, { children: [
9520
+ s.gameOver && /* @__PURE__ */ jsxs15(Box15, { marginTop: 1, children: [
9521
+ /* @__PURE__ */ jsx15(Text16, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
9522
+ /* @__PURE__ */ jsxs15(Text16, { children: [
8146
9523
  " \u5F97\u5206: ",
8147
- /* @__PURE__ */ jsx14(Text15, { color: "yellow", children: s.score }),
9524
+ /* @__PURE__ */ jsx15(Text16, { color: "yellow", children: s.score }),
8148
9525
  " r \u91CD\u5F00 q \u9000\u51FA"
8149
9526
  ] })
8150
9527
  ] }),
8151
- /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
9528
+ /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
8152
9529
  ] });
8153
9530
  }
8154
9531
  var coder_check_default = {
@@ -8158,7 +9535,7 @@ var coder_check_default = {
8158
9535
  play: async () => {
8159
9536
  await new Promise((resolve2) => {
8160
9537
  const { unmount } = render3(
8161
- /* @__PURE__ */ jsx14(CoderCheck, { onExit: () => {
9538
+ /* @__PURE__ */ jsx15(CoderCheck, { onExit: () => {
8162
9539
  unmount();
8163
9540
  resolve2();
8164
9541
  } })
@@ -8538,12 +9915,12 @@ import { render as render4 } from "ink";
8538
9915
  import chalk5 from "chalk";
8539
9916
 
8540
9917
  // src/stock/StockList.tsx
8541
- import { Box as Box15, Text as Text16, useInput as useInput5 } from "ink";
8542
- import { useState as useState9, useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo2 } from "react";
9918
+ import { Box as Box16, Text as Text17, useInput as useInput5 } from "ink";
9919
+ import { useState as useState9, useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo3 } from "react";
8543
9920
  import asciichart from "asciichart";
8544
9921
  import os from "os";
8545
9922
  import { join as join10 } from "path";
8546
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
9923
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
8547
9924
  var SETTINGS_PATH = join10(os.homedir(), ".dskcode", "settings.json");
8548
9925
  function toFileUrl(p) {
8549
9926
  const norm = p.replace(/\\/g, "/");
@@ -8660,7 +10037,7 @@ function StockList({ codes, onExit, onBackToChat }) {
8660
10037
  );
8661
10038
  const [dimMode, setDimMode] = useState9(false);
8662
10039
  const [sortOrder, setSortOrder] = useState9("default");
8663
- const sortedStocks = useMemo2(() => {
10040
+ const sortedStocks = useMemo3(() => {
8664
10041
  if (sortOrder === "default") return stocks;
8665
10042
  return [...stocks].toSorted(
8666
10043
  (a, b) => sortOrder === "desc" ? b.changePercent - a.changePercent : a.changePercent - b.changePercent
@@ -8762,64 +10139,64 @@ function StockList({ codes, onExit, onBackToChat }) {
8762
10139
  );
8763
10140
  if (detailView) {
8764
10141
  if (detailLoading) {
8765
- return /* @__PURE__ */ jsx15(Box15, { paddingLeft: 1, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
10142
+ return /* @__PURE__ */ jsx16(Box16, { paddingLeft: 1, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
8766
10143
  }
8767
10144
  return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
8768
10145
  }
8769
10146
  const cp2 = (c) => dimMode ? { dimColor: true } : { color: c };
8770
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
8771
- /* @__PURE__ */ jsxs15(Box15, { marginBottom: 1, justifyContent: "space-between", children: [
8772
- /* @__PURE__ */ jsx15(Text16, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
8773
- /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
10147
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
10148
+ /* @__PURE__ */ jsxs16(Box16, { marginBottom: 1, justifyContent: "space-between", children: [
10149
+ /* @__PURE__ */ jsx16(Text17, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
10150
+ /* @__PURE__ */ jsxs16(Text17, { dimColor: true, children: [
8774
10151
  " \u{1F550} ",
8775
10152
  currentTime
8776
10153
  ] }),
8777
- /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
10154
+ /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
8778
10155
  ] }),
8779
- /* @__PURE__ */ jsxs15(Box15, { children: [
8780
- /* @__PURE__ */ jsx15(Box15, { width: 3 }),
8781
- /* @__PURE__ */ jsx15(Box15, { width: 9, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u4EE3\u7801" }) }),
8782
- /* @__PURE__ */ jsx15(Box15, { width: 16, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u540D\u79F0" }) }),
8783
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
8784
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
10156
+ /* @__PURE__ */ jsxs16(Box16, { children: [
10157
+ /* @__PURE__ */ jsx16(Box16, { width: 3 }),
10158
+ /* @__PURE__ */ jsx16(Box16, { width: 9, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u4EE3\u7801" }) }),
10159
+ /* @__PURE__ */ jsx16(Box16, { width: 16, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u540D\u79F0" }) }),
10160
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
10161
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsxs16(Text17, { dimColor: true, children: [
8785
10162
  "\u6DA8\u8DCC\u5E45",
8786
10163
  sortOrder === "desc" ? " \u25BC" : sortOrder === "asc" ? " \u25B2" : ""
8787
10164
  ] }) }),
8788
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
8789
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6700\u9AD8" }) }),
8790
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6700\u4F4E" }) }),
8791
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
10165
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
10166
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u6700\u9AD8" }) }),
10167
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u6700\u4F4E" }) }),
10168
+ /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
8792
10169
  ] }),
8793
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
8794
- /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
10170
+ /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
10171
+ /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
8795
10172
  const isSelected = index === selectedIndex;
8796
10173
  const isUp = stock.changePercent >= 0;
8797
10174
  const color = isUp ? "#ff1493" : "#00ff41";
8798
- return /* @__PURE__ */ jsxs15(Box15, { children: [
8799
- /* @__PURE__ */ jsx15(Box15, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx15(Text16, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx15(Text16, { children: " " }) }),
8800
- /* @__PURE__ */ jsx15(Box15, { width: 9, children: /* @__PURE__ */ jsx15(Text16, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
8801
- /* @__PURE__ */ jsx15(Box15, { width: 16, children: /* @__PURE__ */ jsx15(Text16, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
8802
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
8803
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsxs15(Text16, { ...cp2(color), children: [
10175
+ return /* @__PURE__ */ jsxs16(Box16, { children: [
10176
+ /* @__PURE__ */ jsx16(Box16, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx16(Text17, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx16(Text17, { children: " " }) }),
10177
+ /* @__PURE__ */ jsx16(Box16, { width: 9, children: /* @__PURE__ */ jsx16(Text17, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
10178
+ /* @__PURE__ */ jsx16(Box16, { width: 16, children: /* @__PURE__ */ jsx16(Text17, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
10179
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
10180
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsxs16(Text17, { ...cp2(color), children: [
8804
10181
  isUp ? "+" : "",
8805
10182
  stock.changePercent.toFixed(2),
8806
10183
  "%"
8807
10184
  ] }) }),
8808
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsxs15(Text16, { ...cp2(color), children: [
10185
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsxs16(Text17, { ...cp2(color), children: [
8809
10186
  isUp ? "+" : "",
8810
10187
  stock.changeAmount.toFixed(3)
8811
10188
  ] }) }),
8812
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
8813
- /* @__PURE__ */ jsx15(Box15, { width: 12, children: /* @__PURE__ */ jsx15(Text16, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
8814
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text16, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
10189
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
10190
+ /* @__PURE__ */ jsx16(Box16, { width: 12, children: /* @__PURE__ */ jsx16(Text17, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
10191
+ /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsx16(Text17, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
8815
10192
  ] }, stock.code);
8816
10193
  }) }),
8817
- /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 o \u6392\u5E8F h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
8818
- /* @__PURE__ */ jsxs15(Box15, { children: [
8819
- /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
8820
- /* @__PURE__ */ jsx15(Text16, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
10194
+ /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 o \u6392\u5E8F h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
10195
+ /* @__PURE__ */ jsxs16(Box16, { children: [
10196
+ /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
10197
+ /* @__PURE__ */ jsx16(Text17, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
8821
10198
  ] }),
8822
- doubleCtrlC && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text16, { bold: true, ...cp2("#ff1493"), children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
10199
+ doubleCtrlC && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text17, { bold: true, ...cp2("#ff1493"), children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
8823
10200
  ] });
8824
10201
  }
8825
10202
  function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
@@ -8837,33 +10214,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
8837
10214
  raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
8838
10215
  chartLines = raw.split("\n");
8839
10216
  }
8840
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingLeft: 1, children: [
8841
- /* @__PURE__ */ jsxs15(Box15, { marginBottom: 1, justifyContent: "space-between", children: [
8842
- /* @__PURE__ */ jsxs15(Box15, { children: [
8843
- /* @__PURE__ */ jsxs15(Text16, { bold: true, color: "#00ffff", children: [
10217
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", paddingLeft: 1, children: [
10218
+ /* @__PURE__ */ jsxs16(Box16, { marginBottom: 1, justifyContent: "space-between", children: [
10219
+ /* @__PURE__ */ jsxs16(Box16, { children: [
10220
+ /* @__PURE__ */ jsxs16(Text17, { bold: true, color: "#00ffff", children: [
8844
10221
  " \u{1F4CA} ",
8845
10222
  stock.name,
8846
10223
  " "
8847
10224
  ] }),
8848
- /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: stock.code }),
8849
- currentTime && /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
10225
+ /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: stock.code }),
10226
+ currentTime && /* @__PURE__ */ jsxs16(Text17, { dimColor: true, children: [
8850
10227
  " \u{1F550} ",
8851
10228
  currentTime
8852
10229
  ] })
8853
10230
  ] }),
8854
- /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
10231
+ /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
8855
10232
  ] }),
8856
- /* @__PURE__ */ jsxs15(Box15, { children: [
8857
- /* @__PURE__ */ jsx15(Box15, { width: 16, children: /* @__PURE__ */ jsx15(Text16, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
8858
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs15(Text16, { bold: true, color: colorCode, children: [
10233
+ /* @__PURE__ */ jsxs16(Box16, { children: [
10234
+ /* @__PURE__ */ jsx16(Box16, { width: 16, children: /* @__PURE__ */ jsx16(Text17, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
10235
+ /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { bold: true, color: colorCode, children: [
8859
10236
  arrow,
8860
10237
  " ",
8861
10238
  formatPrice(stock.price)
8862
10239
  ] }) })
8863
10240
  ] }),
8864
- /* @__PURE__ */ jsxs15(Box15, { children: [
8865
- /* @__PURE__ */ jsx15(Box15, { width: 16, children: /* @__PURE__ */ jsx15(Text16, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
8866
- /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs15(Text16, { color: colorCode, children: [
10241
+ /* @__PURE__ */ jsxs16(Box16, { children: [
10242
+ /* @__PURE__ */ jsx16(Box16, { width: 16, children: /* @__PURE__ */ jsx16(Text17, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
10243
+ /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: colorCode, children: [
8867
10244
  isUp ? "+" : "",
8868
10245
  stock.changePercent.toFixed(2),
8869
10246
  "%",
@@ -8872,8 +10249,8 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
8872
10249
  stock.changeAmount.toFixed(3)
8873
10250
  ] }) })
8874
10251
  ] }),
8875
- chartLines.length > 0 && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text16, { color: colorCode, children: line || " " }) }, i)) }),
8876
- /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text16, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
10252
+ chartLines.length > 0 && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsx16(Text17, { color: colorCode, children: line || " " }) }, i)) }),
10253
+ /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text17, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
8877
10254
  ] });
8878
10255
  }
8879
10256
 
@@ -8958,7 +10335,7 @@ async function scanProjectFiles(baseDir, dir) {
8958
10335
  }
8959
10336
 
8960
10337
  // src/cli/index.tsx
8961
- import { jsx as jsx16 } from "react/jsx-runtime";
10338
+ import { jsx as jsx17 } from "react/jsx-runtime";
8962
10339
  var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
8963
10340
  function createCli() {
8964
10341
  const program2 = new Command();
@@ -9060,7 +10437,7 @@ compdef _dskcode_completion dskcode`);
9060
10437
  const freshResult = await loadAndValidate();
9061
10438
  const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
9062
10439
  const app = renderApp(
9063
- /* @__PURE__ */ jsx16(
10440
+ /* @__PURE__ */ jsx17(
9064
10441
  StockList,
9065
10442
  {
9066
10443
  codes: codeList,
@@ -9089,7 +10466,7 @@ compdef _dskcode_completion dskcode`);
9089
10466
  }
9090
10467
  const selectedGame = await new Promise((resolve2) => {
9091
10468
  const { unmount } = render4(
9092
- /* @__PURE__ */ jsx16(
10469
+ /* @__PURE__ */ jsx17(
9093
10470
  GamePicker,
9094
10471
  {
9095
10472
  games,
@@ -9129,7 +10506,7 @@ async function startChat(ctx, costTracker) {
9129
10506
  );
9130
10507
  const model = defaultProvider?.model ?? "deepseek-v4-flash";
9131
10508
  const chatApp = renderApp(
9132
- /* @__PURE__ */ jsx16(
10509
+ /* @__PURE__ */ jsx17(
9133
10510
  ChatSession,
9134
10511
  {
9135
10512
  skillCount,
@@ -9147,7 +10524,7 @@ async function startChat(ctx, costTracker) {
9147
10524
  initGames();
9148
10525
  const games = listGames();
9149
10526
  const { unmount } = render4(
9150
- /* @__PURE__ */ jsx16(
10527
+ /* @__PURE__ */ jsx17(
9151
10528
  GamePicker,
9152
10529
  {
9153
10530
  games,
@@ -9171,7 +10548,7 @@ async function startChat(ctx, costTracker) {
9171
10548
  setImmediate(() => {
9172
10549
  const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
9173
10550
  const stockApp = renderApp(
9174
- /* @__PURE__ */ jsx16(
10551
+ /* @__PURE__ */ jsx17(
9175
10552
  StockList,
9176
10553
  {
9177
10554
  codes: defaultStockCodes,