@moonshot-ai/kimi-code 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +1478 -791
- package/dist-web/assets/CodeBlockNode-DWqdMrZf.js +22 -0
- package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-DCpGDnBA.js → CodeBlockShell.vue_vue_type_style_index_0_lang-CP8vJ_jY.js} +1 -1
- package/dist-web/assets/{Tooltip-LgIwf1MF.js → Tooltip-DnT9F5O4.js} +1 -1
- package/dist-web/assets/arc-BTqlVy62.js +1 -0
- package/dist-web/assets/{architectureDiagram-3BPJPVTR-04vShicj.js → architectureDiagram-3BPJPVTR-DOs258PA.js} +1 -1
- package/dist-web/assets/{blockDiagram-GPEHLZMM-ZXXjkQDj.js → blockDiagram-GPEHLZMM-YpbL-evQ.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-jwIjAMhy.js → c4Diagram-AAUBKEIU-BQ0g7h08.js} +1 -1
- package/dist-web/assets/channel-C9wJxpza.js +1 -0
- package/dist-web/assets/{chunk-2J33WTMH-B9WYpP0t.js → chunk-2J33WTMH-nYkFiTji.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-CsLa1hKW.js → chunk-4BX2VUAB-CsmhqO62.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-lg2y1z6m.js → chunk-55IACEB6-CUzTYEsP.js} +1 -1
- package/dist-web/assets/{chunk-727SXJPM-kSXB7Ee2.js → chunk-727SXJPM-BnqEbbXJ.js} +2 -2
- package/dist-web/assets/{chunk-AQP2D5EJ-OzWWS2zu.js → chunk-AQP2D5EJ-DtZARgEl.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-BnloGzDy.js → chunk-FMBD7UC4-R7lWfBOs.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-DQ96qEVb.js → chunk-ND2GUHAM-CZaX1ZXe.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-CnmjyYEF.js → chunk-QZHKN3VN-5MXrqfJe.js} +1 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-DqgKlhvP.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-DqgKlhvP.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-Ca5pGblJ.js → cose-bilkent-S5V4N54A-4f-2zAe6.js} +1 -1
- package/dist-web/assets/{dagre-BM42HDAG-Uh1RPvzw.js → dagre-BM42HDAG-B29IUQLO.js} +1 -1
- package/dist-web/assets/{diagram-2AECGRRQ-BGLdrxMz.js → diagram-2AECGRRQ-BLG8l-kW.js} +1 -1
- package/dist-web/assets/{diagram-5GNKFQAL-dJHq2s9o.js → diagram-5GNKFQAL-D49xpQ0V.js} +1 -1
- package/dist-web/assets/{diagram-KO2AKTUF-Dx74xqL0.js → diagram-KO2AKTUF-CWBzbQ3X.js} +1 -1
- package/dist-web/assets/{diagram-LMA3HP47-D70lxzjc.js → diagram-LMA3HP47-CAcmX_XH.js} +1 -1
- package/dist-web/assets/{diagram-OG6HWLK6-BTNo7COP.js → diagram-OG6HWLK6-BInwpJw8.js} +1 -1
- package/dist-web/assets/{erDiagram-TEJ5UH35-B_7lnLw9.js → erDiagram-TEJ5UH35-Dckcr1cP.js} +1 -1
- package/dist-web/assets/{flowDiagram-I6XJVG4X-CwfNwpue.js → flowDiagram-I6XJVG4X-CjWyYa_0.js} +1 -1
- package/dist-web/assets/{ganttDiagram-6RSMTGT7-BAQE1PPR.js → ganttDiagram-6RSMTGT7-B8-b8RY7.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-DjP3_pYV.js → gitGraphDiagram-PVQCEYII-DF8nQ36i.js} +1 -1
- package/dist-web/assets/index-CKGltB1b.js +9 -0
- package/dist-web/assets/index-Cm9Q4xVi.js +3 -0
- package/dist-web/assets/index-DSjuug95.js +272 -0
- package/dist-web/assets/{index-Cny4-3BH.js → index-Zbw78XRh.js} +1 -1
- package/dist-web/assets/index-sXB6HLvC.css +1 -0
- package/dist-web/assets/{index10-ba-WIDTg.js → index10-Dh4SYXXK.js} +2 -2
- package/dist-web/assets/index11-BIywvtHR.js +9 -0
- package/dist-web/assets/{index5-Bm0FC7PH.js → index5-C_80WRqf.js} +1 -1
- package/dist-web/assets/{index6-CvXSHsNA.js → index6-kJ6dO27T.js} +1 -1
- package/dist-web/assets/{index7-wMYBQdIV.js → index7-Zg1vvU3j.js} +1 -1
- package/dist-web/assets/index8-Bqtk0wsj.js +2 -0
- package/dist-web/assets/{infoDiagram-5YYISTIA-GLnQZOdj.js → infoDiagram-5YYISTIA-m1Ft7ptD.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-BTzUsMXy.js → ishikawaDiagram-YF4QCWOH-Cqe15rN3.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-DftCk-C3.js → journeyDiagram-JHISSGLW-2vdQK870.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-bJiUlhIE.js → kanban-definition-UN3LZRKU-DWcbYK3Y.js} +1 -1
- package/dist-web/assets/{linear-BhksDCJ0.js → linear-DEB4_JxQ.js} +1 -1
- package/dist-web/assets/{mermaid.core-DiVTz3xq.js → mermaid.core-Bo4PXwhd.js} +5 -5
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-3nTQ_Edv.js → mindmap-definition-RKZ34NQL-xhnv3DGe.js} +1 -1
- package/dist-web/assets/{pieDiagram-4H26LBE5-CzfXAqfK.js → pieDiagram-4H26LBE5-Kf-4wCvB.js} +2 -2
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-Be7SO8y7.js → quadrantDiagram-W4KKPZXB-CoEDvyN3.js} +1 -1
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-BBr4uS0C.js → requirementDiagram-4Y6WPE33-C02xoAKl.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-5OEKKPKP-Bp5HEQ3a.js → sankeyDiagram-5OEKKPKP-Cta2--lY.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-3UESZ5HK-d5x9oqBR.js → sequenceDiagram-3UESZ5HK-BGHuv-fJ.js} +1 -1
- package/dist-web/assets/{stateDiagram-AJRCARHV-Hpws1aXz.js → stateDiagram-AJRCARHV-CvYSand1.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-CvSckOje.js +1 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-CatOuZH5.js → timeline-definition-PNZ67QCA-Puu2_su-.js} +1 -1
- package/dist-web/assets/{vennDiagram-CIIHVFJN-Dmka7FWx.js → vennDiagram-CIIHVFJN-C5KnFfg1.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-z5b-tezL.js → vue.runtime.esm-bundler-Cqjpm82Q.js} +1 -1
- package/dist-web/assets/{wardley-L42UT6IY-B-DhFMCA.js → wardley-L42UT6IY-B56jdDEk.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-Dn3fIGpK.js → wardleyDiagram-YWT4CUSO-C5uByZfn.js} +1 -1
- package/dist-web/assets/{xychartDiagram-2RQKCTM6-BKssF4ms.js → xychartDiagram-2RQKCTM6-NW-h6Ru2.js} +1 -1
- package/dist-web/index.html +2 -2
- package/package.json +5 -5
- package/dist-web/assets/CodeBlockNode-B26hAEdG.js +0 -22
- package/dist-web/assets/arc-DZyzZen-.js +0 -1
- package/dist-web/assets/channel-CR6L61dF.js +0 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-BMLEGIwT.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-BMLEGIwT.js +0 -1
- package/dist-web/assets/index-BiWo3nMo.css +0 -1
- package/dist-web/assets/index-CXZzzXGH.js +0 -9
- package/dist-web/assets/index-KEPtQokk.js +0 -268
- package/dist-web/assets/index11-zgOWkVRq.js +0 -9
- package/dist-web/assets/index8-RFRS9KOv.js +0 -2
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-CHpe74LW.js +0 -1
package/dist/main.mjs
CHANGED
|
@@ -73598,7 +73598,7 @@ const TOOL_ERROR_STATUS$2 = "<system>ERROR: Tool execution failed.</system>";
|
|
|
73598
73598
|
const TOOL_EMPTY_STATUS$2 = "<system>Tool output is empty.</system>";
|
|
73599
73599
|
const TOOL_EMPTY_ERROR_STATUS$2 = "<system>ERROR: Tool execution failed. Tool output is empty.</system>";
|
|
73600
73600
|
const TOOL_OUTPUT_EMPTY_TEXT$2 = "Tool output is empty.";
|
|
73601
|
-
const TOOL_INTERRUPTED_ON_RESUME_OUTPUT = "Tool execution was interrupted before its result was recorded. Do not assume the tool completed successfully.";
|
|
73601
|
+
const TOOL_INTERRUPTED_ON_RESUME_OUTPUT$1 = "Tool execution was interrupted before its result was recorded. Do not assume the tool completed successfully.";
|
|
73602
73602
|
var ContextMemory = class {
|
|
73603
73603
|
agent;
|
|
73604
73604
|
_history = [];
|
|
@@ -73770,15 +73770,18 @@ var ContextMemory = class {
|
|
|
73770
73770
|
this.pushHistory(...trimTrailingOpenToolExchange(source.project(source.history)));
|
|
73771
73771
|
}
|
|
73772
73772
|
finishResume() {
|
|
73773
|
-
const interruptedToolCallIds = [...this.pendingToolResultIds];
|
|
73774
73773
|
this.openSteps.clear();
|
|
73775
|
-
|
|
73774
|
+
this.closePendingToolResults();
|
|
73775
|
+
}
|
|
73776
|
+
closePendingToolResults() {
|
|
73777
|
+
if (this.pendingToolResultIds.size === 0) return;
|
|
73778
|
+
const interruptedToolCallIds = [...this.pendingToolResultIds];
|
|
73776
73779
|
for (const toolCallId of interruptedToolCallIds) this.appendLoopEvent({
|
|
73777
73780
|
type: "tool.result",
|
|
73778
73781
|
parentUuid: toolCallId,
|
|
73779
73782
|
toolCallId,
|
|
73780
73783
|
result: {
|
|
73781
|
-
output: TOOL_INTERRUPTED_ON_RESUME_OUTPUT,
|
|
73784
|
+
output: TOOL_INTERRUPTED_ON_RESUME_OUTPUT$1,
|
|
73782
73785
|
isError: true
|
|
73783
73786
|
}
|
|
73784
73787
|
});
|
|
@@ -73790,6 +73793,7 @@ var ContextMemory = class {
|
|
|
73790
73793
|
});
|
|
73791
73794
|
switch (event.type) {
|
|
73792
73795
|
case "step.begin": {
|
|
73796
|
+
this.closePendingToolResults();
|
|
73793
73797
|
const message = {
|
|
73794
73798
|
role: "assistant",
|
|
73795
73799
|
content: [],
|
|
@@ -73835,6 +73839,7 @@ var ContextMemory = class {
|
|
|
73835
73839
|
return;
|
|
73836
73840
|
}
|
|
73837
73841
|
case "tool.result": {
|
|
73842
|
+
if (!this.pendingToolResultIds.has(event.toolCallId)) return;
|
|
73838
73843
|
const message = createToolMessage(event.toolCallId, toolResultOutputForModel(event.result));
|
|
73839
73844
|
this.pushHistory({
|
|
73840
73845
|
...message,
|
|
@@ -105098,6 +105103,32 @@ function renderLine(entry, lineEndingStyle) {
|
|
|
105098
105103
|
function renderedLineBytes(renderedLine, isFirst) {
|
|
105099
105104
|
return (isFirst ? 0 : 1) + Buffer.byteLength(renderedLine, "utf8");
|
|
105100
105105
|
}
|
|
105106
|
+
function renderEntries(entries, lineEndingStyle) {
|
|
105107
|
+
const renderedLines = [];
|
|
105108
|
+
const truncatedLineNumbers = [];
|
|
105109
|
+
let bytes = 0;
|
|
105110
|
+
let maxBytesReached = false;
|
|
105111
|
+
for (const entry of entries) {
|
|
105112
|
+
const rendered = renderLine(entry, lineEndingStyle);
|
|
105113
|
+
const lineBytes = renderedLineBytes(rendered.line, renderedLines.length === 0);
|
|
105114
|
+
if (renderedLines.length > 0 && bytes + lineBytes > 102400) {
|
|
105115
|
+
maxBytesReached = true;
|
|
105116
|
+
break;
|
|
105117
|
+
}
|
|
105118
|
+
if (rendered.wasTruncated) truncatedLineNumbers.push(entry.lineNo);
|
|
105119
|
+
renderedLines.push(rendered.line);
|
|
105120
|
+
bytes += lineBytes;
|
|
105121
|
+
if (bytes >= 102400) {
|
|
105122
|
+
maxBytesReached = true;
|
|
105123
|
+
break;
|
|
105124
|
+
}
|
|
105125
|
+
}
|
|
105126
|
+
return {
|
|
105127
|
+
renderedLines,
|
|
105128
|
+
truncatedLineNumbers,
|
|
105129
|
+
maxBytesReached
|
|
105130
|
+
};
|
|
105131
|
+
}
|
|
105101
105132
|
function isRegularFileMode(stMode) {
|
|
105102
105133
|
return (stMode & S_IFMT$1) === S_IFREG;
|
|
105103
105134
|
}
|
|
@@ -105198,6 +105229,39 @@ var ReadTool = class {
|
|
|
105198
105229
|
}
|
|
105199
105230
|
}
|
|
105200
105231
|
async readForward(safePath, displayPath, lineOffset, effectiveLimit, requestedLines) {
|
|
105232
|
+
const rangeKaos = this.kaos;
|
|
105233
|
+
if (rangeKaos.scanTextFile !== void 0 && rangeKaos.readLineRange !== void 0) {
|
|
105234
|
+
const scan = await rangeKaos.scanTextFile(safePath);
|
|
105235
|
+
if (scan.hasNul) return {
|
|
105236
|
+
isError: true,
|
|
105237
|
+
output: notReadableFileOutput(displayPath)
|
|
105238
|
+
};
|
|
105239
|
+
const selectedEntries = [];
|
|
105240
|
+
let lineNo = lineOffset;
|
|
105241
|
+
for await (const rawLine of rangeKaos.readLineRange(safePath, {
|
|
105242
|
+
startLine: lineOffset,
|
|
105243
|
+
maxLines: effectiveLimit,
|
|
105244
|
+
errors: "strict"
|
|
105245
|
+
})) {
|
|
105246
|
+
selectedEntries.push({
|
|
105247
|
+
lineNo,
|
|
105248
|
+
rawContent: stripTrailingLf(rawLine)
|
|
105249
|
+
});
|
|
105250
|
+
lineNo += 1;
|
|
105251
|
+
}
|
|
105252
|
+
const lineEndingStyle = lineEndingStyleFromFlags(scan.lineEndingFlags);
|
|
105253
|
+
const rendered = renderEntries(selectedEntries, lineEndingStyle);
|
|
105254
|
+
return this.finishReadResult({
|
|
105255
|
+
renderedLines: rendered.renderedLines,
|
|
105256
|
+
truncatedLineNumbers: rendered.truncatedLineNumbers,
|
|
105257
|
+
maxLinesReached: effectiveLimit >= 1e3 && lineOffset + 1e3 <= scan.totalLines,
|
|
105258
|
+
maxBytesReached: rendered.maxBytesReached,
|
|
105259
|
+
lineEndingStyle,
|
|
105260
|
+
startLine: selectedEntries.length > 0 ? lineOffset : 0,
|
|
105261
|
+
totalLines: scan.totalLines,
|
|
105262
|
+
requestedLines
|
|
105263
|
+
});
|
|
105264
|
+
}
|
|
105201
105265
|
const selectedEntries = [];
|
|
105202
105266
|
const flags = {
|
|
105203
105267
|
hasCrLf: false,
|
|
@@ -105231,38 +105295,45 @@ var ReadTool = class {
|
|
|
105231
105295
|
if (selectedEntries.length >= effectiveLimit) collectionClosed = true;
|
|
105232
105296
|
}
|
|
105233
105297
|
const lineEndingStyle = lineEndingStyleFromFlags(flags);
|
|
105234
|
-
const
|
|
105235
|
-
const truncatedLineNumbers = [];
|
|
105236
|
-
let bytes = 0;
|
|
105237
|
-
let maxBytesReached = false;
|
|
105238
|
-
for (const entry of selectedEntries) {
|
|
105239
|
-
const rendered = renderLine(entry, lineEndingStyle);
|
|
105240
|
-
const lineBytes = renderedLineBytes(rendered.line, renderedLines.length === 0);
|
|
105241
|
-
if (renderedLines.length > 0 && bytes + lineBytes > 102400) {
|
|
105242
|
-
maxBytesReached = true;
|
|
105243
|
-
break;
|
|
105244
|
-
}
|
|
105245
|
-
if (rendered.wasTruncated) truncatedLineNumbers.push(entry.lineNo);
|
|
105246
|
-
renderedLines.push(rendered.line);
|
|
105247
|
-
bytes += lineBytes;
|
|
105248
|
-
if (bytes >= 102400) {
|
|
105249
|
-
maxBytesReached = true;
|
|
105250
|
-
break;
|
|
105251
|
-
}
|
|
105252
|
-
}
|
|
105298
|
+
const rendered = renderEntries(selectedEntries, lineEndingStyle);
|
|
105253
105299
|
return this.finishReadResult({
|
|
105254
|
-
renderedLines,
|
|
105255
|
-
truncatedLineNumbers,
|
|
105300
|
+
renderedLines: rendered.renderedLines,
|
|
105301
|
+
truncatedLineNumbers: rendered.truncatedLineNumbers,
|
|
105256
105302
|
maxLinesReached,
|
|
105257
|
-
maxBytesReached,
|
|
105303
|
+
maxBytesReached: rendered.maxBytesReached,
|
|
105258
105304
|
lineEndingStyle,
|
|
105259
|
-
startLine:
|
|
105305
|
+
startLine: selectedEntries.length > 0 ? lineOffset : 0,
|
|
105260
105306
|
totalLines: currentLineNo,
|
|
105261
105307
|
requestedLines
|
|
105262
105308
|
});
|
|
105263
105309
|
}
|
|
105264
105310
|
async readTail(safePath, displayPath, lineOffset, effectiveLimit, requestedLines) {
|
|
105265
105311
|
const tailCount = Math.abs(lineOffset);
|
|
105312
|
+
const rangeKaos = this.kaos;
|
|
105313
|
+
if (rangeKaos.scanTextFile !== void 0 && rangeKaos.readTailLines !== void 0) {
|
|
105314
|
+
const scan = await rangeKaos.scanTextFile(safePath);
|
|
105315
|
+
if (scan.hasNul) return {
|
|
105316
|
+
isError: true,
|
|
105317
|
+
output: notReadableFileOutput(displayPath)
|
|
105318
|
+
};
|
|
105319
|
+
const rawLines = [];
|
|
105320
|
+
for await (const rawLine of rangeKaos.readTailLines(safePath, {
|
|
105321
|
+
tailCount,
|
|
105322
|
+
errors: "strict"
|
|
105323
|
+
})) rawLines.push(rawLine);
|
|
105324
|
+
const startLine = Math.max(1, scan.totalLines - rawLines.length + 1);
|
|
105325
|
+
const entries = rawLines.map((rawLine, index) => ({
|
|
105326
|
+
lineNo: startLine + index,
|
|
105327
|
+
rawContent: stripTrailingLf(rawLine)
|
|
105328
|
+
}));
|
|
105329
|
+
return this.finishTailEntries({
|
|
105330
|
+
entries,
|
|
105331
|
+
lineEndingFlags: scan.lineEndingFlags,
|
|
105332
|
+
effectiveLimit,
|
|
105333
|
+
totalLines: scan.totalLines,
|
|
105334
|
+
requestedLines
|
|
105335
|
+
});
|
|
105336
|
+
}
|
|
105266
105337
|
const entries = [];
|
|
105267
105338
|
const flags = {
|
|
105268
105339
|
hasCrLf: false,
|
|
@@ -105283,8 +105354,17 @@ var ReadTool = class {
|
|
|
105283
105354
|
});
|
|
105284
105355
|
if (entries.length > tailCount) entries.shift();
|
|
105285
105356
|
}
|
|
105286
|
-
|
|
105287
|
-
|
|
105357
|
+
return this.finishTailEntries({
|
|
105358
|
+
entries,
|
|
105359
|
+
lineEndingFlags: flags,
|
|
105360
|
+
effectiveLimit,
|
|
105361
|
+
totalLines: currentLineNo,
|
|
105362
|
+
requestedLines
|
|
105363
|
+
});
|
|
105364
|
+
}
|
|
105365
|
+
finishTailEntries(input) {
|
|
105366
|
+
const lineEndingStyle = lineEndingStyleFromFlags(input.lineEndingFlags);
|
|
105367
|
+
let renderedCandidates = input.entries.slice(0, input.effectiveLimit).map((entry) => {
|
|
105288
105368
|
return {
|
|
105289
105369
|
entry,
|
|
105290
105370
|
rendered: renderLine(entry, lineEndingStyle)
|
|
@@ -105320,8 +105400,8 @@ var ReadTool = class {
|
|
|
105320
105400
|
maxBytesReached,
|
|
105321
105401
|
lineEndingStyle,
|
|
105322
105402
|
startLine: renderedCandidates[0]?.entry.lineNo ?? 0,
|
|
105323
|
-
totalLines:
|
|
105324
|
-
requestedLines
|
|
105403
|
+
totalLines: input.totalLines,
|
|
105404
|
+
requestedLines: input.requestedLines
|
|
105325
105405
|
});
|
|
105326
105406
|
}
|
|
105327
105407
|
finishReadResult(input) {
|
|
@@ -155186,7 +155266,7 @@ function decodeUtf16LeIgnore(data) {
|
|
|
155186
155266
|
* byte-to-character mappings so `errors` has no effect.
|
|
155187
155267
|
* @internal
|
|
155188
155268
|
*/
|
|
155189
|
-
function decodeTextWithErrors(data, encoding, errors = "strict") {
|
|
155269
|
+
function decodeTextWithErrors(data, encoding, errors = "strict", ignoreBOM = false) {
|
|
155190
155270
|
let webLabel;
|
|
155191
155271
|
switch (encoding) {
|
|
155192
155272
|
case "utf-8":
|
|
@@ -155201,9 +155281,15 @@ function decodeTextWithErrors(data, encoding, errors = "strict") {
|
|
|
155201
155281
|
default: webLabel = void 0;
|
|
155202
155282
|
}
|
|
155203
155283
|
if (webLabel === void 0) return data.toString(encoding);
|
|
155204
|
-
if (errors === "strict") return new TextDecoder(webLabel, {
|
|
155284
|
+
if (errors === "strict") return new TextDecoder(webLabel, {
|
|
155285
|
+
fatal: true,
|
|
155286
|
+
ignoreBOM
|
|
155287
|
+
}).decode(data);
|
|
155205
155288
|
if (errors === "ignore") return webLabel === "utf-8" ? decodeUtf8Ignore(data) : decodeUtf16LeIgnore(data);
|
|
155206
|
-
return new TextDecoder(webLabel, {
|
|
155289
|
+
return new TextDecoder(webLabel, {
|
|
155290
|
+
fatal: false,
|
|
155291
|
+
ignoreBOM
|
|
155292
|
+
}).decode(data);
|
|
155207
155293
|
}
|
|
155208
155294
|
/**
|
|
155209
155295
|
* Convert a glob pattern segment (e.g. "*.txt", "file?.log") into a RegExp.
|
|
@@ -155295,6 +155381,7 @@ var BufferedReadable = class extends Readable$1 {
|
|
|
155295
155381
|
//#endregion
|
|
155296
155382
|
//#region ../../packages/kaos/src/local.ts
|
|
155297
155383
|
const isWindows$1 = process.platform === "win32";
|
|
155384
|
+
const READ_CHUNK_SIZE = 64 * 1024;
|
|
155298
155385
|
/**
|
|
155299
155386
|
* Build the `(dev, ino)` cycle-detection key used by `_globWalk`'s
|
|
155300
155387
|
* visited set. Returns `null` when `ino` is 0, which Node returns on
|
|
@@ -155573,12 +155660,143 @@ var LocalKaos = class LocalKaos {
|
|
|
155573
155660
|
const resolved = this._resolvePath(path);
|
|
155574
155661
|
const encoding = options?.encoding ?? "utf-8";
|
|
155575
155662
|
const errors = options?.errors ?? "strict";
|
|
155576
|
-
|
|
155577
|
-
|
|
155578
|
-
|
|
155579
|
-
|
|
155580
|
-
|
|
155581
|
-
|
|
155663
|
+
if (!isUtf8Encoding(encoding)) {
|
|
155664
|
+
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readFile(resolved), encoding, errors));
|
|
155665
|
+
return;
|
|
155666
|
+
}
|
|
155667
|
+
yield* this._readUtf8Lines(resolved, errors);
|
|
155668
|
+
}
|
|
155669
|
+
async scanTextFile(path) {
|
|
155670
|
+
const fh = await open$1(this._resolvePath(path), "r");
|
|
155671
|
+
try {
|
|
155672
|
+
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
155673
|
+
const flags = {
|
|
155674
|
+
hasCrLf: false,
|
|
155675
|
+
hasLf: false,
|
|
155676
|
+
hasLoneCr: false
|
|
155677
|
+
};
|
|
155678
|
+
const validator = createUtf8Validator();
|
|
155679
|
+
let totalLines = 0;
|
|
155680
|
+
let totalBytes = 0;
|
|
155681
|
+
let endsWithNewline = false;
|
|
155682
|
+
let hasNul = false;
|
|
155683
|
+
let prevWasCr = false;
|
|
155684
|
+
while (true) {
|
|
155685
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
155686
|
+
if (bytesRead === 0) break;
|
|
155687
|
+
const chunk = buf.subarray(0, bytesRead);
|
|
155688
|
+
validator.write(chunk);
|
|
155689
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
155690
|
+
const byte = chunk[i];
|
|
155691
|
+
if (byte === void 0) continue;
|
|
155692
|
+
if (byte === 0) hasNul = true;
|
|
155693
|
+
if (byte === 10) totalLines += 1;
|
|
155694
|
+
}
|
|
155695
|
+
prevWasCr = updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr);
|
|
155696
|
+
totalBytes += bytesRead;
|
|
155697
|
+
endsWithNewline = chunk[bytesRead - 1] === 10;
|
|
155698
|
+
}
|
|
155699
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
155700
|
+
validator.end();
|
|
155701
|
+
if (totalBytes > 0 && !endsWithNewline) totalLines += 1;
|
|
155702
|
+
return {
|
|
155703
|
+
totalLines,
|
|
155704
|
+
endsWithNewline,
|
|
155705
|
+
hasNul,
|
|
155706
|
+
lineEndingFlags: flags
|
|
155707
|
+
};
|
|
155708
|
+
} finally {
|
|
155709
|
+
await fh.close();
|
|
155710
|
+
}
|
|
155711
|
+
}
|
|
155712
|
+
async *readLineRange(path, options) {
|
|
155713
|
+
const resolved = this._resolvePath(path);
|
|
155714
|
+
const errors = options.errors ?? "strict";
|
|
155715
|
+
yield* this._readUtf8Lines(resolved, errors, {
|
|
155716
|
+
startLine: options.startLine,
|
|
155717
|
+
maxLines: options.maxLines
|
|
155718
|
+
});
|
|
155719
|
+
}
|
|
155720
|
+
async *readTailLines(path, options) {
|
|
155721
|
+
if (options.tailCount <= 0) return;
|
|
155722
|
+
const resolved = this._resolvePath(path);
|
|
155723
|
+
const errors = options.errors ?? "strict";
|
|
155724
|
+
const fh = await open$1(resolved, "r");
|
|
155725
|
+
try {
|
|
155726
|
+
const s = await fh.stat();
|
|
155727
|
+
if (s.size === 0) return;
|
|
155728
|
+
let pos = s.size;
|
|
155729
|
+
let foundLf = 0;
|
|
155730
|
+
let startOffset = 0;
|
|
155731
|
+
let needLf = options.tailCount;
|
|
155732
|
+
let sawTailBlock = false;
|
|
155733
|
+
while (pos > 0 && foundLf < needLf) {
|
|
155734
|
+
const readSize = Math.min(READ_CHUNK_SIZE, pos);
|
|
155735
|
+
pos -= readSize;
|
|
155736
|
+
const buf = Buffer.alloc(readSize);
|
|
155737
|
+
await fh.read(buf, 0, readSize, pos);
|
|
155738
|
+
if (!sawTailBlock) {
|
|
155739
|
+
sawTailBlock = true;
|
|
155740
|
+
needLf = buf[readSize - 1] === 10 ? options.tailCount + 1 : options.tailCount;
|
|
155741
|
+
}
|
|
155742
|
+
for (let i = readSize - 1; i >= 0; i -= 1) {
|
|
155743
|
+
if (buf[i] !== 10) continue;
|
|
155744
|
+
foundLf += 1;
|
|
155745
|
+
if (foundLf === needLf) {
|
|
155746
|
+
startOffset = pos + i + 1;
|
|
155747
|
+
break;
|
|
155748
|
+
}
|
|
155749
|
+
}
|
|
155750
|
+
}
|
|
155751
|
+
if (foundLf < needLf) startOffset = 0;
|
|
155752
|
+
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readRange(fh, startOffset, s.size - startOffset), "utf-8", errors, startOffset !== 0));
|
|
155753
|
+
} finally {
|
|
155754
|
+
await fh.close();
|
|
155755
|
+
}
|
|
155756
|
+
}
|
|
155757
|
+
async *_readUtf8Lines(resolved, errors, range) {
|
|
155758
|
+
const startLine = range?.startLine ?? 1;
|
|
155759
|
+
const maxLines = range?.maxLines ?? Number.POSITIVE_INFINITY;
|
|
155760
|
+
const fh = await open$1(resolved, "r");
|
|
155761
|
+
try {
|
|
155762
|
+
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
155763
|
+
let pending = [];
|
|
155764
|
+
let pendingOffset = 0;
|
|
155765
|
+
let fileOffset = 0;
|
|
155766
|
+
let lineNo = 1;
|
|
155767
|
+
let yielded = 0;
|
|
155768
|
+
while (true) {
|
|
155769
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
155770
|
+
if (bytesRead === 0) break;
|
|
155771
|
+
const chunk = buf.subarray(0, bytesRead);
|
|
155772
|
+
let lineStart = 0;
|
|
155773
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
155774
|
+
if (chunk[i] !== 10) continue;
|
|
155775
|
+
const piece = chunk.subarray(lineStart, i + 1);
|
|
155776
|
+
const lineOffset = pending.length === 0 ? fileOffset + lineStart : pendingOffset;
|
|
155777
|
+
const line = pending.length === 0 ? piece : Buffer.concat([...pending, piece]);
|
|
155778
|
+
if (lineNo >= startLine) {
|
|
155779
|
+
yield decodeTextWithErrors(line, "utf-8", errors, lineOffset !== 0);
|
|
155780
|
+
yielded += 1;
|
|
155781
|
+
if (yielded >= maxLines) return;
|
|
155782
|
+
}
|
|
155783
|
+
pending = [];
|
|
155784
|
+
lineStart = i + 1;
|
|
155785
|
+
lineNo += 1;
|
|
155786
|
+
}
|
|
155787
|
+
if (lineStart < chunk.length) {
|
|
155788
|
+
const tail = Buffer.from(chunk.subarray(lineStart));
|
|
155789
|
+
if (pending.length === 0) pendingOffset = fileOffset + lineStart;
|
|
155790
|
+
pending.push(tail);
|
|
155791
|
+
}
|
|
155792
|
+
fileOffset += bytesRead;
|
|
155793
|
+
}
|
|
155794
|
+
if (pending.length > 0) {
|
|
155795
|
+
const line = Buffer.concat(pending);
|
|
155796
|
+
if (lineNo >= startLine) yield decodeTextWithErrors(line, "utf-8", errors, pendingOffset !== 0);
|
|
155797
|
+
}
|
|
155798
|
+
} finally {
|
|
155799
|
+
await fh.close();
|
|
155582
155800
|
}
|
|
155583
155801
|
}
|
|
155584
155802
|
async writeBytes(path, data) {
|
|
@@ -155640,6 +155858,90 @@ var LocalKaos = class LocalKaos {
|
|
|
155640
155858
|
return merged;
|
|
155641
155859
|
}
|
|
155642
155860
|
};
|
|
155861
|
+
function isUtf8Encoding(encoding) {
|
|
155862
|
+
return encoding === "utf-8" || encoding === "utf8";
|
|
155863
|
+
}
|
|
155864
|
+
function* splitLinesKeepingTerminator(text) {
|
|
155865
|
+
if (text.length === 0) return;
|
|
155866
|
+
let start = 0;
|
|
155867
|
+
for (let i = 0; i < text.length; i += 1) if (text.codePointAt(i) === 10) {
|
|
155868
|
+
yield text.slice(start, i + 1);
|
|
155869
|
+
start = i + 1;
|
|
155870
|
+
}
|
|
155871
|
+
if (start < text.length) yield text.slice(start);
|
|
155872
|
+
}
|
|
155873
|
+
function updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr) {
|
|
155874
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
155875
|
+
const byte = chunk[i];
|
|
155876
|
+
if (byte === void 0) continue;
|
|
155877
|
+
if (byte === 13) {
|
|
155878
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
155879
|
+
prevWasCr = true;
|
|
155880
|
+
} else if (byte === 10) {
|
|
155881
|
+
if (prevWasCr) flags.hasCrLf = true;
|
|
155882
|
+
else flags.hasLf = true;
|
|
155883
|
+
prevWasCr = false;
|
|
155884
|
+
} else {
|
|
155885
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
155886
|
+
prevWasCr = false;
|
|
155887
|
+
}
|
|
155888
|
+
}
|
|
155889
|
+
return prevWasCr;
|
|
155890
|
+
}
|
|
155891
|
+
function createUtf8Validator() {
|
|
155892
|
+
let needed = 0;
|
|
155893
|
+
let lower = 128;
|
|
155894
|
+
let upper = 191;
|
|
155895
|
+
const fail = () => {
|
|
155896
|
+
throw new TypeError("Invalid UTF-8 data");
|
|
155897
|
+
};
|
|
155898
|
+
return {
|
|
155899
|
+
write(chunk) {
|
|
155900
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
155901
|
+
const byte = chunk[i];
|
|
155902
|
+
if (byte === void 0) continue;
|
|
155903
|
+
if (needed === 0) {
|
|
155904
|
+
if (byte <= 127) continue;
|
|
155905
|
+
if (byte >= 194 && byte <= 223) needed = 1;
|
|
155906
|
+
else if (byte === 224) {
|
|
155907
|
+
needed = 2;
|
|
155908
|
+
lower = 160;
|
|
155909
|
+
} else if (byte >= 225 && byte <= 236) needed = 2;
|
|
155910
|
+
else if (byte === 237) {
|
|
155911
|
+
needed = 2;
|
|
155912
|
+
upper = 159;
|
|
155913
|
+
} else if (byte >= 238 && byte <= 239) needed = 2;
|
|
155914
|
+
else if (byte === 240) {
|
|
155915
|
+
needed = 3;
|
|
155916
|
+
lower = 144;
|
|
155917
|
+
} else if (byte >= 241 && byte <= 243) needed = 3;
|
|
155918
|
+
else if (byte === 244) {
|
|
155919
|
+
needed = 3;
|
|
155920
|
+
upper = 143;
|
|
155921
|
+
} else fail();
|
|
155922
|
+
} else {
|
|
155923
|
+
if (byte < lower || byte > upper) fail();
|
|
155924
|
+
lower = 128;
|
|
155925
|
+
upper = 191;
|
|
155926
|
+
needed -= 1;
|
|
155927
|
+
}
|
|
155928
|
+
}
|
|
155929
|
+
},
|
|
155930
|
+
end() {
|
|
155931
|
+
if (needed !== 0) fail();
|
|
155932
|
+
}
|
|
155933
|
+
};
|
|
155934
|
+
}
|
|
155935
|
+
async function readRange(fh, start, length) {
|
|
155936
|
+
const data = Buffer.alloc(length);
|
|
155937
|
+
let offset = 0;
|
|
155938
|
+
while (offset < length) {
|
|
155939
|
+
const { bytesRead } = await fh.read(data, offset, length - offset, start + offset);
|
|
155940
|
+
if (bytesRead === 0) break;
|
|
155941
|
+
offset += bytesRead;
|
|
155942
|
+
}
|
|
155943
|
+
return offset === length ? data : data.subarray(0, offset);
|
|
155944
|
+
}
|
|
155643
155945
|
function waitForSpawn(child) {
|
|
155644
155946
|
return new Promise((resolve, reject) => {
|
|
155645
155947
|
const onSpawn = () => {
|
|
@@ -155729,7 +156031,7 @@ var KimiCore = class {
|
|
|
155729
156031
|
}), options.mcpServers);
|
|
155730
156032
|
const parentKaos = overrides.kaos ?? await this.getKaos();
|
|
155731
156033
|
const persistenceKaos = overrides.persistenceKaos ?? parentKaos;
|
|
155732
|
-
const localWorkspaceDirs = await readWorkspaceAdditionalDirs(
|
|
156034
|
+
const localWorkspaceDirs = await readWorkspaceAdditionalDirs(persistenceKaos, workDir);
|
|
155733
156035
|
const callerAdditionalDirs = await resolveWorkspaceAdditionalDirs(parentKaos, workDir, options.additionalDirs ?? []);
|
|
155734
156036
|
const additionalDirs = normalizeAdditionalDirs([...localWorkspaceDirs.additionalDirs, ...callerAdditionalDirs]);
|
|
155735
156037
|
const summary = await this.sessionStore.create({
|
|
@@ -155819,7 +156121,7 @@ var KimiCore = class {
|
|
|
155819
156121
|
async resumeSessionWithOverrides(input, overrides) {
|
|
155820
156122
|
const summary = await this.sessionStore.get(input.sessionId);
|
|
155821
156123
|
const parentKaosForRead = overrides.kaos ?? await this.getKaos();
|
|
155822
|
-
const localWorkspaceDirs = await readWorkspaceAdditionalDirs(parentKaosForRead, summary.workDir);
|
|
156124
|
+
const localWorkspaceDirs = await readWorkspaceAdditionalDirs(overrides.persistenceKaos ?? parentKaosForRead, summary.workDir);
|
|
155823
156125
|
const callerAdditionalDirs = await resolveWorkspaceAdditionalDirs(parentKaosForRead, summary.workDir, input.additionalDirs ?? []);
|
|
155824
156126
|
const additionalDirs = normalizeAdditionalDirs([...localWorkspaceDirs.additionalDirs, ...callerAdditionalDirs]);
|
|
155825
156127
|
const active = this.sessions.get(summary.id);
|
|
@@ -161019,7 +161321,7 @@ function buildItem(item, parentIdx) {
|
|
|
161019
161321
|
if (item.header !== void 0) out.header = item.header;
|
|
161020
161322
|
if (item.body !== void 0) out.body = item.body;
|
|
161021
161323
|
if (item.multiSelect !== void 0) out.multi_select = item.multiSelect;
|
|
161022
|
-
|
|
161324
|
+
out.allow_other = true;
|
|
161023
161325
|
if (item.otherLabel !== void 0) out.other_label = item.otherLabel;
|
|
161024
161326
|
if (item.otherDescription !== void 0) out.other_description = item.otherDescription;
|
|
161025
161327
|
return out;
|
|
@@ -162835,9 +163137,9 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162835
163137
|
filterSet.add(safe.relative);
|
|
162836
163138
|
}
|
|
162837
163139
|
}
|
|
162838
|
-
const insideRes = await runCommand$
|
|
163140
|
+
const insideRes = await runCommand$2("git", ["rev-parse", "--is-inside-work-tree"], realCwd);
|
|
162839
163141
|
if (insideRes.exitCode !== 0 || insideRes.stdout.trim() !== "true") throw new FsGitUnavailableError(realCwd, insideRes.stderr.trim() || `git rev-parse exit ${insideRes.exitCode}`);
|
|
162840
|
-
const porcRes = await runCommand$
|
|
163142
|
+
const porcRes = await runCommand$2("git", [
|
|
162841
163143
|
"status",
|
|
162842
163144
|
"--porcelain=v1",
|
|
162843
163145
|
"--branch"
|
|
@@ -162845,13 +163147,13 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162845
163147
|
if (porcRes.exitCode !== 0) throw new FsGitUnavailableError(realCwd, porcRes.stderr.trim() || `git status exit ${porcRes.exitCode}`);
|
|
162846
163148
|
const result = parsePorcelain(porcRes.stdout, filterSet);
|
|
162847
163149
|
if (porcRes.stdout.split("\n").some((line) => line.length > 0 && !line.startsWith("## "))) {
|
|
162848
|
-
if ((await runCommand$
|
|
163150
|
+
if ((await runCommand$2("git", [
|
|
162849
163151
|
"rev-parse",
|
|
162850
163152
|
"--verify",
|
|
162851
163153
|
"--quiet",
|
|
162852
163154
|
"HEAD"
|
|
162853
163155
|
], realCwd)).exitCode === 0) {
|
|
162854
|
-
const numstatRes = await runCommand$
|
|
163156
|
+
const numstatRes = await runCommand$2("git", [
|
|
162855
163157
|
"diff",
|
|
162856
163158
|
"--no-color",
|
|
162857
163159
|
"--numstat",
|
|
@@ -162872,7 +163174,7 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162872
163174
|
const cached = this.pullRequestCache.get(cwd);
|
|
162873
163175
|
const now = Date.now();
|
|
162874
163176
|
if (cached !== void 0 && now - cached.fetchedAt < PULL_REQUEST_TTL_MS$1) return cached.value;
|
|
162875
|
-
const res = await runCommand$
|
|
163177
|
+
const res = await runCommand$2("gh", [
|
|
162876
163178
|
"pr",
|
|
162877
163179
|
"view",
|
|
162878
163180
|
"--json",
|
|
@@ -162896,9 +163198,9 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162896
163198
|
const realCwd = await promises.realpath(cwd);
|
|
162897
163199
|
const safe = await resolveSafePath(realCwd, req.path);
|
|
162898
163200
|
const rel = safe.relative;
|
|
162899
|
-
const insideRes = await runCommand$
|
|
163201
|
+
const insideRes = await runCommand$2("git", ["rev-parse", "--is-inside-work-tree"], realCwd);
|
|
162900
163202
|
if (insideRes.exitCode !== 0 || insideRes.stdout.trim() !== "true") throw new FsGitUnavailableError(realCwd, insideRes.stderr.trim() || `git rev-parse exit ${insideRes.exitCode}`);
|
|
162901
|
-
const statusRes = await runCommand$
|
|
163203
|
+
const statusRes = await runCommand$2("git", [
|
|
162902
163204
|
"status",
|
|
162903
163205
|
"--porcelain=v1",
|
|
162904
163206
|
"--",
|
|
@@ -162906,7 +163208,7 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162906
163208
|
], realCwd);
|
|
162907
163209
|
if (statusRes.exitCode !== 0) throw new FsGitUnavailableError(realCwd, statusRes.stderr.trim() || `git status exit ${statusRes.exitCode}`);
|
|
162908
163210
|
const untracked = statusRes.stdout.startsWith("??");
|
|
162909
|
-
const hasHead = (await runCommand$
|
|
163211
|
+
const hasHead = (await runCommand$2("git", [
|
|
162910
163212
|
"rev-parse",
|
|
162911
163213
|
"--verify",
|
|
162912
163214
|
"--quiet",
|
|
@@ -162914,7 +163216,7 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162914
163216
|
], realCwd)).exitCode === 0;
|
|
162915
163217
|
let diffRes;
|
|
162916
163218
|
if (untracked || !hasHead) {
|
|
162917
|
-
diffRes = await runCommand$
|
|
163219
|
+
diffRes = await runCommand$2("git", [
|
|
162918
163220
|
"diff",
|
|
162919
163221
|
"--no-color",
|
|
162920
163222
|
"--no-index",
|
|
@@ -162924,7 +163226,7 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162924
163226
|
], realCwd);
|
|
162925
163227
|
if (diffRes.exitCode !== 0 && diffRes.exitCode !== 1) throw new FsGitUnavailableError(realCwd, diffRes.stderr.trim() || `git diff exit ${diffRes.exitCode}`);
|
|
162926
163228
|
} else {
|
|
162927
|
-
diffRes = await runCommand$
|
|
163229
|
+
diffRes = await runCommand$2("git", [
|
|
162928
163230
|
"diff",
|
|
162929
163231
|
"--no-color",
|
|
162930
163232
|
"HEAD",
|
|
@@ -162946,7 +163248,7 @@ let FsGitService = class FsGitService extends Disposable {
|
|
|
162946
163248
|
}
|
|
162947
163249
|
};
|
|
162948
163250
|
FsGitService = __decorate([__decorateParam(0, ISessionService)], FsGitService);
|
|
162949
|
-
async function runCommand$
|
|
163251
|
+
async function runCommand$2(cmd, args, cwd, options = {}) {
|
|
162950
163252
|
return new Promise((resolve) => {
|
|
162951
163253
|
const child = spawn$1(cmd, args, {
|
|
162952
163254
|
cwd,
|
|
@@ -166901,6 +167203,7 @@ const TOOL_ERROR_STATUS$1 = "<system>ERROR: Tool execution failed.</system>";
|
|
|
166901
167203
|
const TOOL_EMPTY_STATUS$1 = "<system>Tool output is empty.</system>";
|
|
166902
167204
|
const TOOL_EMPTY_ERROR_STATUS$1 = "<system>ERROR: Tool execution failed. Tool output is empty.</system>";
|
|
166903
167205
|
const TOOL_OUTPUT_EMPTY_TEXT$1 = "Tool output is empty.";
|
|
167206
|
+
const TOOL_INTERRUPTED_ON_RESUME_OUTPUT = "Tool execution was interrupted before its result was recorded. Do not assume the tool completed successfully.";
|
|
166904
167207
|
/**
|
|
166905
167208
|
* Reduce wire records into the full transcript. Pure (no I/O); exported for
|
|
166906
167209
|
* tests. Unknown or non-context records are ignored — only `context.*`
|
|
@@ -166924,6 +167227,27 @@ function reduceWireRecords(records) {
|
|
|
166924
167227
|
push(...deferred);
|
|
166925
167228
|
deferred = [];
|
|
166926
167229
|
};
|
|
167230
|
+
const closePendingToolResults = (time) => {
|
|
167231
|
+
if (pendingToolResultIds.size === 0) return;
|
|
167232
|
+
const interruptedToolCallIds = [...pendingToolResultIds];
|
|
167233
|
+
for (const toolCallId of interruptedToolCallIds) {
|
|
167234
|
+
push({
|
|
167235
|
+
message: {
|
|
167236
|
+
role: "tool",
|
|
167237
|
+
content: toolResultContent({
|
|
167238
|
+
output: TOOL_INTERRUPTED_ON_RESUME_OUTPUT,
|
|
167239
|
+
isError: true
|
|
167240
|
+
}),
|
|
167241
|
+
toolCalls: [],
|
|
167242
|
+
toolCallId,
|
|
167243
|
+
isError: true
|
|
167244
|
+
},
|
|
167245
|
+
time
|
|
167246
|
+
});
|
|
167247
|
+
pendingToolResultIds.delete(toolCallId);
|
|
167248
|
+
}
|
|
167249
|
+
flushDeferredIfToolExchangeClosed();
|
|
167250
|
+
};
|
|
166927
167251
|
const resetOpenState = () => {
|
|
166928
167252
|
openSteps.clear();
|
|
166929
167253
|
pendingToolResultIds.clear();
|
|
@@ -166932,6 +167256,7 @@ function reduceWireRecords(records) {
|
|
|
166932
167256
|
const applyLoopEvent = (event, time) => {
|
|
166933
167257
|
switch (event.type) {
|
|
166934
167258
|
case "step.begin": {
|
|
167259
|
+
closePendingToolResults(time);
|
|
166935
167260
|
const entry = {
|
|
166936
167261
|
message: {
|
|
166937
167262
|
role: "assistant",
|
|
@@ -166964,6 +167289,7 @@ function reduceWireRecords(records) {
|
|
|
166964
167289
|
return;
|
|
166965
167290
|
}
|
|
166966
167291
|
case "tool.result":
|
|
167292
|
+
if (!pendingToolResultIds.has(event.toolCallId)) return;
|
|
166967
167293
|
push({
|
|
166968
167294
|
message: {
|
|
166969
167295
|
role: "tool",
|
|
@@ -175651,7 +175977,7 @@ function getCapabilities() {
|
|
|
175651
175977
|
}
|
|
175652
175978
|
const KITTY_PREFIX = "\x1B_G";
|
|
175653
175979
|
const ITERM2_PREFIX = "\x1B]1337;File=";
|
|
175654
|
-
function isImageLine(line) {
|
|
175980
|
+
function isImageLine$1(line) {
|
|
175655
175981
|
if (line.startsWith(KITTY_PREFIX) || line.startsWith(ITERM2_PREFIX)) return true;
|
|
175656
175982
|
return line.includes(KITTY_PREFIX) || line.includes(ITERM2_PREFIX);
|
|
175657
175983
|
}
|
|
@@ -176327,7 +176653,7 @@ var TUI = class TUI extends Container {
|
|
|
176327
176653
|
const reset = TUI.SEGMENT_RESET;
|
|
176328
176654
|
for (let i = 0; i < lines.length; i++) {
|
|
176329
176655
|
const line = lines[i];
|
|
176330
|
-
if (!isImageLine(line)) lines[i] = normalizeTerminalOutput(line) + reset;
|
|
176656
|
+
if (!isImageLine$1(line)) lines[i] = normalizeTerminalOutput(line) + reset;
|
|
176331
176657
|
}
|
|
176332
176658
|
return lines;
|
|
176333
176659
|
}
|
|
@@ -176355,7 +176681,7 @@ var TUI = class TUI extends Container {
|
|
|
176355
176681
|
}
|
|
176356
176682
|
/** Splice overlay content into a base line at a specific column. Single-pass optimized. */
|
|
176357
176683
|
compositeLineAt(baseLine, overlayLine, startCol, overlayWidth, totalWidth) {
|
|
176358
|
-
if (isImageLine(baseLine)) return baseLine;
|
|
176684
|
+
if (isImageLine$1(baseLine)) return baseLine;
|
|
176359
176685
|
const afterStart = startCol + overlayWidth;
|
|
176360
176686
|
const base = extractSegments(baseLine, startCol, afterStart, totalWidth - afterStart, true);
|
|
176361
176687
|
const overlay = sliceWithWidth(overlayLine, 0, overlayWidth, true);
|
|
@@ -176551,7 +176877,7 @@ var TUI = class TUI extends Container {
|
|
|
176551
176877
|
if (i > firstChanged) buffer += "\r\n";
|
|
176552
176878
|
buffer += "\x1B[2K";
|
|
176553
176879
|
const line = newLines[i];
|
|
176554
|
-
if (!isImageLine(line) && visibleWidth(line) > width) {
|
|
176880
|
+
if (!isImageLine$1(line) && visibleWidth(line) > width) {
|
|
176555
176881
|
const crashLogPath = path$14.join(nodeOs.homedir(), ".pi", "agent", "pi-crash.log");
|
|
176556
176882
|
const crashData = [
|
|
176557
176883
|
`Crash at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -180552,14 +180878,14 @@ var Markdown = class {
|
|
|
180552
180878
|
renderedLines.push(...tokenLines);
|
|
180553
180879
|
}
|
|
180554
180880
|
const wrappedLines = [];
|
|
180555
|
-
for (const line of renderedLines) if (isImageLine(line)) wrappedLines.push(line);
|
|
180881
|
+
for (const line of renderedLines) if (isImageLine$1(line)) wrappedLines.push(line);
|
|
180556
180882
|
else wrappedLines.push(...wrapTextWithAnsi(line, contentWidth));
|
|
180557
180883
|
const leftMargin = " ".repeat(this.paddingX);
|
|
180558
180884
|
const rightMargin = " ".repeat(this.paddingX);
|
|
180559
180885
|
const bgFn = this.defaultTextStyle?.bgColor;
|
|
180560
180886
|
const contentLines = [];
|
|
180561
180887
|
for (const line of wrappedLines) {
|
|
180562
|
-
if (isImageLine(line)) {
|
|
180888
|
+
if (isImageLine$1(line)) {
|
|
180563
180889
|
contentLines.push(line);
|
|
180564
180890
|
continue;
|
|
180565
180891
|
}
|
|
@@ -241951,7 +242277,7 @@ function waitForSigint() {
|
|
|
241951
242277
|
//#region src/cli/commands.ts
|
|
241952
242278
|
function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}, onUpgrade = () => {}) {
|
|
241953
242279
|
const program = new Command(CLI_COMMAND_NAME).description("The Starting Point for Next-Gen Agents").version(version, "-V, --version").allowUnknownOption(false).configureHelp({ helpWidth: 100 }).helpOption("-h, --help", "Show help.").usage("[options] [command]").addHelpText("after", "\nDocumentation: https://moonshotai.github.io/kimi-code/\n");
|
|
241954
|
-
program.addOption(new Option("-S, --session [id]", "Resume a session. With ID: resume that session. Without ID: interactively pick.").argParser((val) => val === true ? "" : val)).addOption(new Option("-r, --resume [id]").hideHelp().argParser((val) => val === true ? "" : val)).option("-
|
|
242280
|
+
program.addOption(new Option("-S, --session [id]", "Resume a session. With ID: resume that session. Without ID: interactively pick.").argParser((val) => val === true ? "" : val)).addOption(new Option("-r, --resume [id]").hideHelp().argParser((val) => val === true ? "" : val)).option("-c, --continue", "Continue the previous session for the working directory.", false).addOption(new Option("-C").hideHelp().default(false)).option("-y, --yolo", "Automatically approve all actions.", false).option("--auto", "Start in auto permission mode.", false).addOption(new Option("-m, --model <model>", "LLM model alias to use for this invocation. Defaults to default_model in config.toml.")).addOption(new Option("-p, --prompt <prompt>", "Run one prompt non-interactively and print the response.")).addOption(new Option("--output-format <format>", "Output format for prompt mode. Defaults to text.").choices(["text", "stream-json"])).addOption(new Option("--skills-dir <dir>", "Load skills from this directory instead of auto-discovered user and project directories. Can be repeated.").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--add-dir <dir>", "Add an additional workspace directory for this session. Can be repeated.").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--yes").hideHelp().default(false)).addOption(new Option("--auto-approve").hideHelp().default(false)).option("--plan", "Start in plan mode.", false);
|
|
241955
242281
|
registerExportCommand(program);
|
|
241956
242282
|
registerProviderCommand(program);
|
|
241957
242283
|
registerAcpCommand(program);
|
|
@@ -241975,7 +242301,7 @@ function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}
|
|
|
241975
242301
|
const autoValue = raw["auto"] === true;
|
|
241976
242302
|
onMain({
|
|
241977
242303
|
session: sessionValue,
|
|
241978
|
-
continue: raw["continue"],
|
|
242304
|
+
continue: raw["continue"] === true || raw["C"] === true,
|
|
241979
242305
|
yolo: yoloValue,
|
|
241980
242306
|
auto: autoValue,
|
|
241981
242307
|
plan: raw["plan"],
|
|
@@ -243330,6 +243656,15 @@ var ModelSelectorComponent = class extends Container {
|
|
|
243330
243656
|
alias: selected.alias,
|
|
243331
243657
|
thinking: effectiveThinking(selected.model, this.draftFor(selected))
|
|
243332
243658
|
});
|
|
243659
|
+
return;
|
|
243660
|
+
}
|
|
243661
|
+
if (matchesKey(data, Key.alt("s")) && this.opts.onSessionOnlySelect !== void 0) {
|
|
243662
|
+
const selected = this.selectedChoice();
|
|
243663
|
+
if (selected === void 0) return;
|
|
243664
|
+
this.opts.onSessionOnlySelect({
|
|
243665
|
+
alias: selected.alias,
|
|
243666
|
+
thinking: effectiveThinking(selected.model, this.draftFor(selected))
|
|
243667
|
+
});
|
|
243333
243668
|
}
|
|
243334
243669
|
}
|
|
243335
243670
|
render(width) {
|
|
@@ -243341,7 +243676,9 @@ var ModelSelectorComponent = class extends Container {
|
|
|
243341
243676
|
if (this.opts.providerSwitchHint) hintParts.push("Tab toggle provider");
|
|
243342
243677
|
hintParts.push("↑↓ navigate");
|
|
243343
243678
|
if (searchable && view.query.length > 0) hintParts.push("Backspace clear");
|
|
243344
|
-
hintParts.push("Enter select"
|
|
243679
|
+
hintParts.push("Enter select");
|
|
243680
|
+
if (this.opts.onSessionOnlySelect !== void 0) hintParts.push("Alt+S session-only");
|
|
243681
|
+
hintParts.push("Esc cancel");
|
|
243345
243682
|
const lines = [
|
|
243346
243683
|
currentTheme.fg("primary", "─".repeat(width)),
|
|
243347
243684
|
currentTheme.boldFg("primary", " Select a model") + titleSuffix,
|
|
@@ -244044,6 +244381,7 @@ function makeSelector(opts, subset) {
|
|
|
244044
244381
|
searchable: true,
|
|
244045
244382
|
providerSwitchHint: true,
|
|
244046
244383
|
onSelect: opts.onSelect,
|
|
244384
|
+
onSessionOnlySelect: opts.onSessionOnlySelect,
|
|
244047
244385
|
onCancel: opts.onCancel
|
|
244048
244386
|
});
|
|
244049
244387
|
}
|
|
@@ -244912,14 +245250,18 @@ function showModelPicker(host, selectedValue = host.state.appState.model) {
|
|
|
244912
245250
|
currentThinking: host.state.appState.thinking,
|
|
244913
245251
|
onSelect: ({ alias, thinking }) => {
|
|
244914
245252
|
host.restoreEditor();
|
|
244915
|
-
performModelSwitch(host, alias, thinking);
|
|
245253
|
+
performModelSwitch(host, alias, thinking, true);
|
|
245254
|
+
},
|
|
245255
|
+
onSessionOnlySelect: ({ alias, thinking }) => {
|
|
245256
|
+
host.restoreEditor();
|
|
245257
|
+
performModelSwitch(host, alias, thinking, false);
|
|
244916
245258
|
},
|
|
244917
245259
|
onCancel: () => {
|
|
244918
245260
|
host.restoreEditor();
|
|
244919
245261
|
}
|
|
244920
245262
|
}));
|
|
244921
245263
|
}
|
|
244922
|
-
async function performModelSwitch(host, alias, thinking) {
|
|
245264
|
+
async function performModelSwitch(host, alias, thinking, persist) {
|
|
244923
245265
|
if (host.state.appState.streamingPhase !== "idle") {
|
|
244924
245266
|
host.showError("Cannot switch models while streaming — press Esc or Ctrl-C first.");
|
|
244925
245267
|
return;
|
|
@@ -244949,14 +245291,17 @@ async function performModelSwitch(host, alias, thinking) {
|
|
|
244949
245291
|
if (thinking !== prevThinking) host.track("thinking_toggle", { enabled: thinking });
|
|
244950
245292
|
}
|
|
244951
245293
|
let persisted = false;
|
|
244952
|
-
try {
|
|
245294
|
+
if (persist) try {
|
|
244953
245295
|
persisted = await persistModelSelection(host, alias, thinking);
|
|
244954
245296
|
} catch (error) {
|
|
244955
245297
|
const msg = formatErrorMessage$2(error);
|
|
244956
245298
|
host.showError(`Switched to ${alias}, but failed to save default: ${msg}`);
|
|
244957
245299
|
return;
|
|
244958
245300
|
}
|
|
244959
|
-
|
|
245301
|
+
let status;
|
|
245302
|
+
if (runtimeChanged) status = persist ? `Switched to ${alias} with thinking ${level}.` : `Switched to ${alias} with thinking ${level} for this session only.`;
|
|
245303
|
+
else if (persist && persisted) status = `Saved ${alias} with thinking ${level} as default.`;
|
|
245304
|
+
else status = `Already using ${alias} with thinking ${level}.`;
|
|
244960
245305
|
host.showStatus(status, "success");
|
|
244961
245306
|
}
|
|
244962
245307
|
async function persistModelSelection(host, alias, thinking) {
|
|
@@ -248872,16 +249217,18 @@ var CompactionComponent = class extends Container {
|
|
|
248872
249217
|
ui;
|
|
248873
249218
|
headerText;
|
|
248874
249219
|
instruction;
|
|
249220
|
+
tip;
|
|
248875
249221
|
blinkOn = true;
|
|
248876
249222
|
blinkTimer = null;
|
|
248877
249223
|
done = false;
|
|
248878
249224
|
canceled = false;
|
|
248879
249225
|
tokensBefore;
|
|
248880
249226
|
tokensAfter;
|
|
248881
|
-
constructor(ui, instruction) {
|
|
249227
|
+
constructor(ui, instruction, tip) {
|
|
248882
249228
|
super();
|
|
248883
249229
|
this.ui = ui;
|
|
248884
249230
|
this.instruction = instruction;
|
|
249231
|
+
this.tip = tip;
|
|
248885
249232
|
this.addChild(new Spacer(1));
|
|
248886
249233
|
this.headerText = new Text(this.buildHeader(), 0, 0);
|
|
248887
249234
|
this.addChild(this.headerText);
|
|
@@ -248921,7 +249268,7 @@ var CompactionComponent = class extends Container {
|
|
|
248921
249268
|
buildHeader() {
|
|
248922
249269
|
if (this.done) return `${currentTheme.fg("success", STATUS_BULLET)}${currentTheme.boldFg("success", "Compaction complete")}${this.tokensBefore !== void 0 && this.tokensAfter !== void 0 ? currentTheme.dim(` (${String(this.tokensBefore)} → ${String(this.tokensAfter)} tokens)`) : ""}`;
|
|
248923
249270
|
if (this.canceled) return `${currentTheme.fg("warning", STATUS_BULLET)}${currentTheme.boldFg("warning", "Compaction cancelled")}`;
|
|
248924
|
-
return `${this.blinkOn ? currentTheme.fg("text", STATUS_BULLET) : " "}${currentTheme.boldFg("primary", "Compacting context...")}`;
|
|
249271
|
+
return `${this.blinkOn ? currentTheme.fg("text", STATUS_BULLET) : " "}${currentTheme.boldFg("primary", "Compacting context...")}${this.tip ? currentTheme.fg("textDim", ` · Tip: ${this.tip}`) : ""}`;
|
|
248925
249272
|
}
|
|
248926
249273
|
startBlink() {
|
|
248927
249274
|
this.blinkTimer = setInterval(() => {
|
|
@@ -312326,6 +312673,14 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
312326
312673
|
...shouldCap ? { maxLines: 10 } : {}
|
|
312327
312674
|
});
|
|
312328
312675
|
for (const line of lines) this.addChild(new Text(line, 2, 0));
|
|
312676
|
+
} else if (name === "Bash" && this.result === void 0) {
|
|
312677
|
+
const command = str(this.toolCall.args["command"]);
|
|
312678
|
+
if (command.length === 0) return;
|
|
312679
|
+
this.addChild(new ShellExecutionComponent({
|
|
312680
|
+
command,
|
|
312681
|
+
showCommand: true,
|
|
312682
|
+
commandPreviewLines: this.expanded ? void 0 : 10
|
|
312683
|
+
}));
|
|
312329
312684
|
}
|
|
312330
312685
|
}
|
|
312331
312686
|
/**
|
|
@@ -312603,9 +312958,15 @@ var UserMessageComponent = class {
|
|
|
312603
312958
|
const imageLines = thumbnail.render(contentWidth);
|
|
312604
312959
|
for (const line of imageLines) lines.push(" ".repeat(bulletWidth) + line);
|
|
312605
312960
|
}
|
|
312606
|
-
return lines.map((line) =>
|
|
312961
|
+
return lines.map((line) => {
|
|
312962
|
+
if (isImageLine(line)) return line;
|
|
312963
|
+
return truncateToWidth(line, safeWidth, "…");
|
|
312964
|
+
});
|
|
312607
312965
|
}
|
|
312608
312966
|
};
|
|
312967
|
+
function isImageLine(line) {
|
|
312968
|
+
return line.includes("\x1B_G") || line.includes("\x1B]1337;File=");
|
|
312969
|
+
}
|
|
312609
312970
|
//#endregion
|
|
312610
312971
|
//#region src/tui/utils/transcript-component-metadata.ts
|
|
312611
312972
|
const componentEntries = /* @__PURE__ */ new WeakMap();
|
|
@@ -314452,6 +314813,8 @@ var MoonLoader = class extends Text {
|
|
|
314452
314813
|
colorFn;
|
|
314453
314814
|
label;
|
|
314454
314815
|
displayText = "";
|
|
314816
|
+
tip = "";
|
|
314817
|
+
availableWidth = 0;
|
|
314455
314818
|
constructor(ui, style = "moon", colorFn, label = "") {
|
|
314456
314819
|
super("", 1, 0);
|
|
314457
314820
|
this.ui = ui;
|
|
@@ -314482,18 +314845,713 @@ var MoonLoader = class extends Text {
|
|
|
314482
314845
|
this.colorFn = colorFn;
|
|
314483
314846
|
this.updateDisplay();
|
|
314484
314847
|
}
|
|
314848
|
+
setTip(tip) {
|
|
314849
|
+
this.tip = tip;
|
|
314850
|
+
this.updateDisplay();
|
|
314851
|
+
}
|
|
314852
|
+
setAvailableWidth(width) {
|
|
314853
|
+
if (this.availableWidth === width) return;
|
|
314854
|
+
this.availableWidth = width;
|
|
314855
|
+
this.updateDisplay();
|
|
314856
|
+
}
|
|
314485
314857
|
renderInline() {
|
|
314486
314858
|
return this.displayText;
|
|
314487
314859
|
}
|
|
314488
314860
|
updateDisplay() {
|
|
314489
314861
|
const frame = this.frames[this.currentFrame];
|
|
314490
314862
|
const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
|
|
314491
|
-
|
|
314863
|
+
const baseText = this.label ? `${coloredFrame} ${this.label}` : coloredFrame;
|
|
314864
|
+
let text = baseText;
|
|
314865
|
+
if (this.tip) {
|
|
314866
|
+
const withTip = baseText + currentTheme.fg("textDim", this.tip);
|
|
314867
|
+
if (this.availableWidth === 0 || visibleWidth(withTip) <= this.availableWidth) text = withTip;
|
|
314868
|
+
}
|
|
314869
|
+
this.displayText = text;
|
|
314492
314870
|
this.setText(this.displayText);
|
|
314493
314871
|
this.ui.requestRender();
|
|
314494
314872
|
}
|
|
314495
314873
|
};
|
|
314496
314874
|
//#endregion
|
|
314875
|
+
//#region src/tui/constant/tips.ts
|
|
314876
|
+
/**
|
|
314877
|
+
* Subset of toolbar tips shown behind the composing spinner.
|
|
314878
|
+
*/
|
|
314879
|
+
const WORKING_TIPS = [
|
|
314880
|
+
{
|
|
314881
|
+
text: "ctrl-s to add guidance without waiting for the turn to finish",
|
|
314882
|
+
priority: 2,
|
|
314883
|
+
solo: true
|
|
314884
|
+
},
|
|
314885
|
+
{
|
|
314886
|
+
text: "/tasks to check progress and status for background tasks",
|
|
314887
|
+
priority: 2
|
|
314888
|
+
},
|
|
314889
|
+
{
|
|
314890
|
+
text: "/init: generate AGENTS.md",
|
|
314891
|
+
priority: 2
|
|
314892
|
+
},
|
|
314893
|
+
{ text: "Try /dance for a hidden Easter egg" },
|
|
314894
|
+
{
|
|
314895
|
+
text: "/plugins: manage plugins — try the \"superpowers\" plugin",
|
|
314896
|
+
solo: true,
|
|
314897
|
+
priority: 3
|
|
314898
|
+
},
|
|
314899
|
+
{
|
|
314900
|
+
text: "/plugins: manage plugins — try the \"Kimi Datasource\" for reliable financial, economic, and academic data",
|
|
314901
|
+
solo: true,
|
|
314902
|
+
priority: 3
|
|
314903
|
+
},
|
|
314904
|
+
{
|
|
314905
|
+
text: "ask Kimi to schedule tasks, e.g. \"remind me at 5pm\"",
|
|
314906
|
+
solo: true,
|
|
314907
|
+
priority: 3
|
|
314908
|
+
},
|
|
314909
|
+
{
|
|
314910
|
+
text: "/sessions to browse and resume earlier sessions",
|
|
314911
|
+
solo: true
|
|
314912
|
+
},
|
|
314913
|
+
{
|
|
314914
|
+
text: "/goal for multi-step work with a clear finish line",
|
|
314915
|
+
priority: 2,
|
|
314916
|
+
solo: true
|
|
314917
|
+
},
|
|
314918
|
+
{
|
|
314919
|
+
text: "/goal next to queue follow-up work while the current goal keeps running",
|
|
314920
|
+
solo: true
|
|
314921
|
+
},
|
|
314922
|
+
{
|
|
314923
|
+
text: "/web: use the Web UI for a better experience",
|
|
314924
|
+
solo: true
|
|
314925
|
+
},
|
|
314926
|
+
{
|
|
314927
|
+
text: "@: mention files",
|
|
314928
|
+
priority: 2
|
|
314929
|
+
}
|
|
314930
|
+
];
|
|
314931
|
+
const ALL_TIPS = [
|
|
314932
|
+
...WORKING_TIPS,
|
|
314933
|
+
{ text: "shift+enter: newline" },
|
|
314934
|
+
{ text: "ctrl+c: cancel" },
|
|
314935
|
+
{ text: "/theme to switch the terminal UI theme" },
|
|
314936
|
+
{ text: "/auto when you want Kimi to handle approvals and keep going unattended" },
|
|
314937
|
+
{ text: "/yolo to skip most approvals for trusted batch work, only use it in repos you trust" },
|
|
314938
|
+
{ text: "/help: show commands" },
|
|
314939
|
+
{
|
|
314940
|
+
text: "/compact compresses context when it gets long",
|
|
314941
|
+
priority: 2
|
|
314942
|
+
},
|
|
314943
|
+
{
|
|
314944
|
+
text: "ctrl-o to hide or reveal tool output switching between a clean chat view and full execution details",
|
|
314945
|
+
priority: 2
|
|
314946
|
+
},
|
|
314947
|
+
{
|
|
314948
|
+
text: "shift-tab to Plan mode to review the approach before Kimi edits files.",
|
|
314949
|
+
priority: 2
|
|
314950
|
+
},
|
|
314951
|
+
{
|
|
314952
|
+
text: "/model: switch model",
|
|
314953
|
+
priority: 2
|
|
314954
|
+
}
|
|
314955
|
+
];
|
|
314956
|
+
//#endregion
|
|
314957
|
+
//#region src/utils/git/git-status.ts
|
|
314958
|
+
/**
|
|
314959
|
+
* Cached git branch + working-tree status for the footer/statusline.
|
|
314960
|
+
*
|
|
314961
|
+
* Branch name refreshes every 5s, porcelain status every 15s. Branch
|
|
314962
|
+
* and status reads stay synchronous with short timeouts. Pull request
|
|
314963
|
+
* lookup uses an async cache so a slow `gh pr view` never blocks
|
|
314964
|
+
* footer rendering.
|
|
314965
|
+
*/
|
|
314966
|
+
const BRANCH_TTL_MS = 5e3;
|
|
314967
|
+
const STATUS_TTL_MS = 15e3;
|
|
314968
|
+
const PULL_REQUEST_TTL_MS = 6e4;
|
|
314969
|
+
const SPAWN_TIMEOUT_MS = 500;
|
|
314970
|
+
const PR_SPAWN_TIMEOUT_MS = 5e3;
|
|
314971
|
+
const AHEAD_BEHIND_RE = /\[(?:ahead (\d+))?(?:, )?(?:behind (\d+))?\]/;
|
|
314972
|
+
function createGitStatusCache(workDir, options = {}) {
|
|
314973
|
+
const isRepo = detectGitRepo(workDir);
|
|
314974
|
+
let branch = {
|
|
314975
|
+
value: null,
|
|
314976
|
+
fetchedAt: 0
|
|
314977
|
+
};
|
|
314978
|
+
let status = {
|
|
314979
|
+
dirty: false,
|
|
314980
|
+
ahead: 0,
|
|
314981
|
+
behind: 0,
|
|
314982
|
+
diffAdded: 0,
|
|
314983
|
+
diffDeleted: 0,
|
|
314984
|
+
fetchedAt: 0
|
|
314985
|
+
};
|
|
314986
|
+
let pullRequest = {
|
|
314987
|
+
value: null,
|
|
314988
|
+
branch: null,
|
|
314989
|
+
fetchedAt: 0,
|
|
314990
|
+
pendingBranch: null,
|
|
314991
|
+
requestId: 0
|
|
314992
|
+
};
|
|
314993
|
+
return { getStatus: () => {
|
|
314994
|
+
if (!isRepo) return null;
|
|
314995
|
+
const now = Date.now();
|
|
314996
|
+
if (now - branch.fetchedAt >= BRANCH_TTL_MS) branch = {
|
|
314997
|
+
value: readBranch(workDir),
|
|
314998
|
+
fetchedAt: now
|
|
314999
|
+
};
|
|
315000
|
+
if (branch.value === null) return null;
|
|
315001
|
+
if (now - status.fetchedAt >= STATUS_TTL_MS) status = {
|
|
315002
|
+
...readStatus(workDir),
|
|
315003
|
+
fetchedAt: now
|
|
315004
|
+
};
|
|
315005
|
+
refreshPullRequestIfNeeded(branch.value, now);
|
|
315006
|
+
return {
|
|
315007
|
+
branch: branch.value,
|
|
315008
|
+
dirty: status.dirty,
|
|
315009
|
+
ahead: status.ahead,
|
|
315010
|
+
behind: status.behind,
|
|
315011
|
+
diffAdded: status.diffAdded,
|
|
315012
|
+
diffDeleted: status.diffDeleted,
|
|
315013
|
+
pullRequest: pullRequest.branch === branch.value ? pullRequest.value : null
|
|
315014
|
+
};
|
|
315015
|
+
} };
|
|
315016
|
+
function refreshPullRequestIfNeeded(branchName, now) {
|
|
315017
|
+
if (pullRequest.pendingBranch === branchName) return;
|
|
315018
|
+
const fetchedAt = pullRequest.branch === branchName ? pullRequest.fetchedAt : 0;
|
|
315019
|
+
if (now - fetchedAt < PULL_REQUEST_TTL_MS) return;
|
|
315020
|
+
const requestId = pullRequest.requestId + 1;
|
|
315021
|
+
pullRequest = {
|
|
315022
|
+
value: pullRequest.branch === branchName ? pullRequest.value : null,
|
|
315023
|
+
branch: branchName,
|
|
315024
|
+
fetchedAt,
|
|
315025
|
+
pendingBranch: branchName,
|
|
315026
|
+
requestId
|
|
315027
|
+
};
|
|
315028
|
+
readPullRequest(workDir).then((value) => {
|
|
315029
|
+
if (pullRequest.requestId !== requestId) return;
|
|
315030
|
+
const changed = !samePullRequest(pullRequest.branch === branchName ? pullRequest.value : null, value);
|
|
315031
|
+
pullRequest = {
|
|
315032
|
+
value,
|
|
315033
|
+
branch: branchName,
|
|
315034
|
+
fetchedAt: Date.now(),
|
|
315035
|
+
pendingBranch: null,
|
|
315036
|
+
requestId
|
|
315037
|
+
};
|
|
315038
|
+
if (changed) options.onChange?.();
|
|
315039
|
+
});
|
|
315040
|
+
}
|
|
315041
|
+
}
|
|
315042
|
+
function detectGitRepo(workDir) {
|
|
315043
|
+
try {
|
|
315044
|
+
const result = spawnSync("git", [
|
|
315045
|
+
"-C",
|
|
315046
|
+
workDir,
|
|
315047
|
+
"rev-parse",
|
|
315048
|
+
"--is-inside-work-tree"
|
|
315049
|
+
], {
|
|
315050
|
+
encoding: "utf8",
|
|
315051
|
+
timeout: SPAWN_TIMEOUT_MS
|
|
315052
|
+
});
|
|
315053
|
+
return result.status === 0 && result.stdout.trim() === "true";
|
|
315054
|
+
} catch {
|
|
315055
|
+
return false;
|
|
315056
|
+
}
|
|
315057
|
+
}
|
|
315058
|
+
function readBranch(workDir) {
|
|
315059
|
+
try {
|
|
315060
|
+
const result = spawnSync("git", [
|
|
315061
|
+
"-C",
|
|
315062
|
+
workDir,
|
|
315063
|
+
"branch",
|
|
315064
|
+
"--show-current"
|
|
315065
|
+
], {
|
|
315066
|
+
encoding: "utf8",
|
|
315067
|
+
timeout: SPAWN_TIMEOUT_MS
|
|
315068
|
+
});
|
|
315069
|
+
if (result.status !== 0) return null;
|
|
315070
|
+
const name = result.stdout.trim();
|
|
315071
|
+
return name.length > 0 ? name : null;
|
|
315072
|
+
} catch {
|
|
315073
|
+
return null;
|
|
315074
|
+
}
|
|
315075
|
+
}
|
|
315076
|
+
function readStatus(workDir) {
|
|
315077
|
+
try {
|
|
315078
|
+
const result = spawnSync("git", [
|
|
315079
|
+
"-C",
|
|
315080
|
+
workDir,
|
|
315081
|
+
"status",
|
|
315082
|
+
"--porcelain",
|
|
315083
|
+
"-b"
|
|
315084
|
+
], {
|
|
315085
|
+
encoding: "utf8",
|
|
315086
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
315087
|
+
maxBuffer: 4 * 1024 * 1024
|
|
315088
|
+
});
|
|
315089
|
+
if (result.status !== 0) return {
|
|
315090
|
+
dirty: false,
|
|
315091
|
+
ahead: 0,
|
|
315092
|
+
behind: 0,
|
|
315093
|
+
diffAdded: 0,
|
|
315094
|
+
diffDeleted: 0
|
|
315095
|
+
};
|
|
315096
|
+
let dirty = false;
|
|
315097
|
+
let ahead = 0;
|
|
315098
|
+
let behind = 0;
|
|
315099
|
+
for (const line of result.stdout.split("\n")) if (line.startsWith("## ")) {
|
|
315100
|
+
const m = AHEAD_BEHIND_RE.exec(line);
|
|
315101
|
+
if (m) {
|
|
315102
|
+
ahead = Number.parseInt(m[1] ?? "0", 10) || 0;
|
|
315103
|
+
behind = Number.parseInt(m[2] ?? "0", 10) || 0;
|
|
315104
|
+
}
|
|
315105
|
+
} else if (line.trim().length > 0) dirty = true;
|
|
315106
|
+
const diff = dirty ? readDiffStats(workDir) : {
|
|
315107
|
+
added: 0,
|
|
315108
|
+
deleted: 0
|
|
315109
|
+
};
|
|
315110
|
+
return {
|
|
315111
|
+
dirty,
|
|
315112
|
+
ahead,
|
|
315113
|
+
behind,
|
|
315114
|
+
diffAdded: diff.added,
|
|
315115
|
+
diffDeleted: diff.deleted
|
|
315116
|
+
};
|
|
315117
|
+
} catch {
|
|
315118
|
+
return {
|
|
315119
|
+
dirty: false,
|
|
315120
|
+
ahead: 0,
|
|
315121
|
+
behind: 0,
|
|
315122
|
+
diffAdded: 0,
|
|
315123
|
+
diffDeleted: 0
|
|
315124
|
+
};
|
|
315125
|
+
}
|
|
315126
|
+
}
|
|
315127
|
+
function readDiffStats(workDir) {
|
|
315128
|
+
try {
|
|
315129
|
+
const result = spawnSync("git", [
|
|
315130
|
+
"-C",
|
|
315131
|
+
workDir,
|
|
315132
|
+
"diff",
|
|
315133
|
+
"--numstat",
|
|
315134
|
+
"HEAD",
|
|
315135
|
+
"--"
|
|
315136
|
+
], {
|
|
315137
|
+
encoding: "utf8",
|
|
315138
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
315139
|
+
maxBuffer: 4 * 1024 * 1024
|
|
315140
|
+
});
|
|
315141
|
+
if (result.status !== 0) return {
|
|
315142
|
+
added: 0,
|
|
315143
|
+
deleted: 0
|
|
315144
|
+
};
|
|
315145
|
+
let added = 0;
|
|
315146
|
+
let deleted = 0;
|
|
315147
|
+
for (const line of result.stdout.split("\n")) {
|
|
315148
|
+
if (!line) continue;
|
|
315149
|
+
const [addedText, deletedText] = line.split(" ");
|
|
315150
|
+
added += parseDiffNumstatCount(addedText);
|
|
315151
|
+
deleted += parseDiffNumstatCount(deletedText);
|
|
315152
|
+
}
|
|
315153
|
+
return {
|
|
315154
|
+
added,
|
|
315155
|
+
deleted
|
|
315156
|
+
};
|
|
315157
|
+
} catch {
|
|
315158
|
+
return {
|
|
315159
|
+
added: 0,
|
|
315160
|
+
deleted: 0
|
|
315161
|
+
};
|
|
315162
|
+
}
|
|
315163
|
+
}
|
|
315164
|
+
function parseDiffNumstatCount(value) {
|
|
315165
|
+
if (value === void 0 || value === "-") return 0;
|
|
315166
|
+
const n = Number.parseInt(value, 10);
|
|
315167
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
315168
|
+
}
|
|
315169
|
+
function readPullRequest(workDir) {
|
|
315170
|
+
return new Promise((resolve) => {
|
|
315171
|
+
try {
|
|
315172
|
+
execFile("gh", [
|
|
315173
|
+
"pr",
|
|
315174
|
+
"view",
|
|
315175
|
+
"--json",
|
|
315176
|
+
"number,url"
|
|
315177
|
+
], {
|
|
315178
|
+
cwd: workDir,
|
|
315179
|
+
encoding: "utf8",
|
|
315180
|
+
env: {
|
|
315181
|
+
...process.env,
|
|
315182
|
+
GH_NO_UPDATE_NOTIFIER: "1",
|
|
315183
|
+
GH_PROMPT_DISABLED: "1"
|
|
315184
|
+
},
|
|
315185
|
+
timeout: PR_SPAWN_TIMEOUT_MS,
|
|
315186
|
+
maxBuffer: 256 * 1024
|
|
315187
|
+
}, (error, stdout) => {
|
|
315188
|
+
if (error !== null) {
|
|
315189
|
+
resolve(null);
|
|
315190
|
+
return;
|
|
315191
|
+
}
|
|
315192
|
+
resolve(parsePullRequest(stdout));
|
|
315193
|
+
});
|
|
315194
|
+
} catch {
|
|
315195
|
+
resolve(null);
|
|
315196
|
+
}
|
|
315197
|
+
});
|
|
315198
|
+
}
|
|
315199
|
+
function samePullRequest(a, b) {
|
|
315200
|
+
if (a === null || b === null) return a === b;
|
|
315201
|
+
return a.number === b.number && a.url === b.url;
|
|
315202
|
+
}
|
|
315203
|
+
function parsePullRequest(stdout) {
|
|
315204
|
+
try {
|
|
315205
|
+
const raw = JSON.parse(stdout);
|
|
315206
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
315207
|
+
const record = raw;
|
|
315208
|
+
const number = record["number"];
|
|
315209
|
+
const url = record["url"];
|
|
315210
|
+
if (typeof number !== "number" || !Number.isInteger(number) || number <= 0) return null;
|
|
315211
|
+
if (typeof url !== "string" || !isSafeHttpUrl(url)) return null;
|
|
315212
|
+
return {
|
|
315213
|
+
number,
|
|
315214
|
+
url
|
|
315215
|
+
};
|
|
315216
|
+
} catch {
|
|
315217
|
+
return null;
|
|
315218
|
+
}
|
|
315219
|
+
}
|
|
315220
|
+
function isSafeHttpUrl(value) {
|
|
315221
|
+
if (hasControlChars(value)) return false;
|
|
315222
|
+
try {
|
|
315223
|
+
const url = new URL(value);
|
|
315224
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
315225
|
+
} catch {
|
|
315226
|
+
return false;
|
|
315227
|
+
}
|
|
315228
|
+
}
|
|
315229
|
+
function hasControlChars(value) {
|
|
315230
|
+
for (const char of value) {
|
|
315231
|
+
const code = char.codePointAt(0) ?? 0;
|
|
315232
|
+
if (code <= 31 || code === 127) return true;
|
|
315233
|
+
}
|
|
315234
|
+
return false;
|
|
315235
|
+
}
|
|
315236
|
+
function formatGitBadgeBase(status) {
|
|
315237
|
+
const parts = [];
|
|
315238
|
+
const diff = formatDiffStats(status);
|
|
315239
|
+
if (diff) parts.push(diff);
|
|
315240
|
+
let sync = "";
|
|
315241
|
+
if (status.ahead > 0) sync += `↑${status.ahead}`;
|
|
315242
|
+
if (status.behind > 0) sync += `↓${status.behind}`;
|
|
315243
|
+
if (sync) parts.push(sync);
|
|
315244
|
+
return parts.length === 0 ? status.branch : `${status.branch} [${parts.join(" ")}]`;
|
|
315245
|
+
}
|
|
315246
|
+
function formatPullRequestBadge(pullRequest, options = {}) {
|
|
315247
|
+
const prText = `[PR#${String(pullRequest.number)}]`;
|
|
315248
|
+
return options.linkPullRequest ? toTerminalHyperlink(prText, pullRequest.url) : prText;
|
|
315249
|
+
}
|
|
315250
|
+
function formatDiffStats(status) {
|
|
315251
|
+
const parts = [];
|
|
315252
|
+
if (status.diffAdded > 0) parts.push(`+${String(status.diffAdded)}`);
|
|
315253
|
+
if (status.diffDeleted > 0) parts.push(`-${String(status.diffDeleted)}`);
|
|
315254
|
+
if (parts.length > 0) return parts.join(" ");
|
|
315255
|
+
return status.dirty ? "±" : null;
|
|
315256
|
+
}
|
|
315257
|
+
function toTerminalHyperlink(text, url) {
|
|
315258
|
+
if (!isSafeHttpUrl(url)) return text;
|
|
315259
|
+
return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
|
|
315260
|
+
}
|
|
315261
|
+
//#endregion
|
|
315262
|
+
//#region src/tui/components/chrome/footer.ts
|
|
315263
|
+
const MAX_CWD_SEGMENTS = 3;
|
|
315264
|
+
const GOAL_TIMER_INTERVAL_MS = 1e3;
|
|
315265
|
+
const TIP_ROTATE_INTERVAL_MS$1 = 1e4;
|
|
315266
|
+
const TIP_SEPARATOR = " | ";
|
|
315267
|
+
/**
|
|
315268
|
+
* Expand tips into a rotation sequence using smooth weighted round-robin
|
|
315269
|
+
* (the nginx SWRR algorithm). Higher-`priority` tips appear more often while
|
|
315270
|
+
* staying evenly spread, so a tip generally does not land next to its own
|
|
315271
|
+
* duplicate. Deterministic and computed once at module load. Exported for
|
|
315272
|
+
* unit testing.
|
|
315273
|
+
*/
|
|
315274
|
+
function buildWeightedTips(tips) {
|
|
315275
|
+
const items = tips.map((t) => ({
|
|
315276
|
+
tip: t,
|
|
315277
|
+
weight: Math.max(1, Math.trunc(t.priority ?? 1)),
|
|
315278
|
+
current: 0
|
|
315279
|
+
}));
|
|
315280
|
+
const total = items.reduce((sum, it) => sum + it.weight, 0);
|
|
315281
|
+
const seq = [];
|
|
315282
|
+
for (let n = 0; n < total; n++) {
|
|
315283
|
+
let best = items[0];
|
|
315284
|
+
for (const it of items) {
|
|
315285
|
+
it.current += it.weight;
|
|
315286
|
+
if (it.current > best.current) best = it;
|
|
315287
|
+
}
|
|
315288
|
+
best.current -= total;
|
|
315289
|
+
seq.push(best.tip);
|
|
315290
|
+
}
|
|
315291
|
+
return seq;
|
|
315292
|
+
}
|
|
315293
|
+
const ROTATION = buildWeightedTips(ALL_TIPS);
|
|
315294
|
+
function currentTipIndex() {
|
|
315295
|
+
return Math.floor(Date.now() / TIP_ROTATE_INTERVAL_MS$1);
|
|
315296
|
+
}
|
|
315297
|
+
/**
|
|
315298
|
+
* Pick the tip(s) for a rotation index over the weighted ROTATION sequence.
|
|
315299
|
+
* `primary` is always shown when it fits; `pair` (primary + next tip joined
|
|
315300
|
+
* by the separator) is offered for wide terminals. Pairing is skipped when
|
|
315301
|
+
* the current/next tip is `solo` or when the neighbour is a duplicate of the
|
|
315302
|
+
* current tip (which can happen at the wrap boundary), keeping long/important
|
|
315303
|
+
* tips on their own and avoiding "X | X".
|
|
315304
|
+
*/
|
|
315305
|
+
function tipsForIndex(index) {
|
|
315306
|
+
const n = ROTATION.length;
|
|
315307
|
+
if (n === 0) return {
|
|
315308
|
+
primary: "",
|
|
315309
|
+
pair: null
|
|
315310
|
+
};
|
|
315311
|
+
const offset = (index % n + n) % n;
|
|
315312
|
+
const current = ROTATION[offset];
|
|
315313
|
+
if (n === 1 || current.solo) return {
|
|
315314
|
+
primary: current.text,
|
|
315315
|
+
pair: null
|
|
315316
|
+
};
|
|
315317
|
+
const next = ROTATION[(offset + 1) % n];
|
|
315318
|
+
if (next.solo || next.text === current.text) return {
|
|
315319
|
+
primary: current.text,
|
|
315320
|
+
pair: null
|
|
315321
|
+
};
|
|
315322
|
+
return {
|
|
315323
|
+
primary: current.text,
|
|
315324
|
+
pair: current.text + TIP_SEPARATOR + next.text
|
|
315325
|
+
};
|
|
315326
|
+
}
|
|
315327
|
+
/**
|
|
315328
|
+
* Footer goal badge, e.g. `[goal ● active · 4m · 7 turns]`. Only shown for a
|
|
315329
|
+
* live (active/paused) goal; terminal/no goal -> no badge. Turn count is a raw
|
|
315330
|
+
* count unless an explicit turn budget is set, in which case it shows used/limit.
|
|
315331
|
+
*/
|
|
315332
|
+
function formatGoalBadge(goal, colors, wallClockMs) {
|
|
315333
|
+
if (goal === null || goal === void 0) return null;
|
|
315334
|
+
if (goal.status !== "active" && goal.status !== "paused" && goal.status !== "blocked") return null;
|
|
315335
|
+
const dotColor = goal.status === "active" ? colors.primary : goal.status === "blocked" ? colors.warning : colors.textMuted;
|
|
315336
|
+
const turns = goal.budget.turnBudget !== null ? `${goal.turnsUsed}/${goal.budget.turnBudget} turns` : `${goal.turnsUsed} ${goal.turnsUsed === 1 ? "turn" : "turns"}`;
|
|
315337
|
+
const label = `${goal.status} · ${formatBadgeElapsed(wallClockMs ?? goal.wallClockMs)} · ${turns}`;
|
|
315338
|
+
return chalk.hex(colors.textMuted)("[goal ") + chalk.hex(dotColor)("●") + chalk.hex(colors.textMuted)(` ${label}]`);
|
|
315339
|
+
}
|
|
315340
|
+
function formatBadgeElapsed(ms) {
|
|
315341
|
+
const totalSeconds = Math.round(ms / 1e3);
|
|
315342
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
315343
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
315344
|
+
if (minutes < 60) return `${minutes}m`;
|
|
315345
|
+
return `${Math.floor(minutes / 60)}h${minutes % 60}m`;
|
|
315346
|
+
}
|
|
315347
|
+
function modelDisplayName(state) {
|
|
315348
|
+
const model = state.availableModels[state.model];
|
|
315349
|
+
return model?.displayName ?? model?.model ?? state.model;
|
|
315350
|
+
}
|
|
315351
|
+
function shortenCwd(path) {
|
|
315352
|
+
if (!path) return path;
|
|
315353
|
+
const home = process.env["HOME"] ?? "";
|
|
315354
|
+
let work = path;
|
|
315355
|
+
if (home && path === home) return "~";
|
|
315356
|
+
if (home && path.startsWith(home + "/")) work = "~" + path.slice(home.length);
|
|
315357
|
+
const segments = work.split("/").filter((s) => s.length > 0);
|
|
315358
|
+
if (segments.length <= MAX_CWD_SEGMENTS) return work;
|
|
315359
|
+
return `…/${segments.slice(-MAX_CWD_SEGMENTS).join("/")}`;
|
|
315360
|
+
}
|
|
315361
|
+
function formatTokenCount(n) {
|
|
315362
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
315363
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
315364
|
+
return String(n);
|
|
315365
|
+
}
|
|
315366
|
+
function safeUsage(usage) {
|
|
315367
|
+
return safeUsageRatio(usage);
|
|
315368
|
+
}
|
|
315369
|
+
function formatContextStatus(usage, tokens, maxTokens) {
|
|
315370
|
+
const pct = `${(safeUsage(usage) * 100).toFixed(1)}%`;
|
|
315371
|
+
if (maxTokens && maxTokens > 0 && tokens !== void 0) return `context: ${pct} (${formatTokenCount(tokens)}/${formatTokenCount(maxTokens)})`;
|
|
315372
|
+
return `context: ${pct}`;
|
|
315373
|
+
}
|
|
315374
|
+
function formatFooterGitBadge(status, colors) {
|
|
315375
|
+
const base = chalk.hex(colors.textDim)(formatGitBadgeBase(status));
|
|
315376
|
+
if (status.pullRequest === null) return base;
|
|
315377
|
+
return `${base} ${chalk.hex(colors.primary)(formatPullRequestBadge(status.pullRequest, { linkPullRequest: true }))}`;
|
|
315378
|
+
}
|
|
315379
|
+
var FooterComponent = class {
|
|
315380
|
+
state;
|
|
315381
|
+
onRefresh;
|
|
315382
|
+
gitCache;
|
|
315383
|
+
gitCacheWorkDir;
|
|
315384
|
+
transientHint = null;
|
|
315385
|
+
goalSnapshotKey = null;
|
|
315386
|
+
goalObservedAtMs = Date.now();
|
|
315387
|
+
goalTimer = null;
|
|
315388
|
+
/**
|
|
315389
|
+
* Non-terminal background-task counts split by kind so the footer can
|
|
315390
|
+
* render two distinct badges. `bashTasks` covers `bash-*` BPM tasks
|
|
315391
|
+
* spawned via `Shell run_in_background=true`; `agentTasks` covers
|
|
315392
|
+
* `agent-*` BPM tasks (background subagents). Either zero hides its
|
|
315393
|
+
* respective badge.
|
|
315394
|
+
*/
|
|
315395
|
+
backgroundBashTaskCount = 0;
|
|
315396
|
+
backgroundAgentCount = 0;
|
|
315397
|
+
constructor(state, onRefresh = () => {}) {
|
|
315398
|
+
this.state = state;
|
|
315399
|
+
this.onRefresh = onRefresh;
|
|
315400
|
+
this.gitCacheWorkDir = state.workDir;
|
|
315401
|
+
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onRefresh });
|
|
315402
|
+
this.syncGoalClock(state.goal);
|
|
315403
|
+
this.syncGoalTimer(state.goal);
|
|
315404
|
+
}
|
|
315405
|
+
setState(state) {
|
|
315406
|
+
if (state.workDir !== this.gitCacheWorkDir) {
|
|
315407
|
+
this.gitCacheWorkDir = state.workDir;
|
|
315408
|
+
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onRefresh });
|
|
315409
|
+
}
|
|
315410
|
+
this.syncGoalClock(state.goal);
|
|
315411
|
+
this.syncGoalTimer(state.goal);
|
|
315412
|
+
this.state = state;
|
|
315413
|
+
}
|
|
315414
|
+
/**
|
|
315415
|
+
* Short-lived hint that replaces the rotating toolbar tips on line 1.
|
|
315416
|
+
* Used by the exit-confirmation double-tap flow to show "Press Ctrl+C
|
|
315417
|
+
* again to exit" without requiring a toast/overlay subsystem.
|
|
315418
|
+
* Pass `null` to clear.
|
|
315419
|
+
*/
|
|
315420
|
+
setTransientHint(hint) {
|
|
315421
|
+
this.transientHint = hint;
|
|
315422
|
+
}
|
|
315423
|
+
getTransientHint() {
|
|
315424
|
+
return this.transientHint;
|
|
315425
|
+
}
|
|
315426
|
+
/**
|
|
315427
|
+
* Sync both background-task badges with live counts. Each non-zero
|
|
315428
|
+
* count produces its own bracketed badge on line 1; zeros hide them
|
|
315429
|
+
* independently.
|
|
315430
|
+
*/
|
|
315431
|
+
setBackgroundCounts(counts) {
|
|
315432
|
+
this.backgroundBashTaskCount = Math.max(0, counts.bashTasks);
|
|
315433
|
+
this.backgroundAgentCount = Math.max(0, counts.agentTasks);
|
|
315434
|
+
}
|
|
315435
|
+
invalidate() {}
|
|
315436
|
+
render(width) {
|
|
315437
|
+
const colors = currentTheme.palette;
|
|
315438
|
+
const state = this.state;
|
|
315439
|
+
const left = [];
|
|
315440
|
+
const modes = [];
|
|
315441
|
+
if (state.permissionMode === "auto") modes.push(chalk.hex(colors.warning).bold("auto"));
|
|
315442
|
+
if (state.permissionMode === "yolo") modes.push(chalk.hex(colors.warning).bold("yolo"));
|
|
315443
|
+
if (state.planMode) modes.push(chalk.hex(colors.primary).bold("plan"));
|
|
315444
|
+
if (state.swarmMode) modes.push(chalk.hex(colors.accent).bold("swarm"));
|
|
315445
|
+
if (modes.length > 0) left.push(modes.join(" "));
|
|
315446
|
+
const goalBadge = formatGoalBadge(state.goal, colors, this.goalWallClockMs(state.goal));
|
|
315447
|
+
if (goalBadge !== null) left.push(goalBadge);
|
|
315448
|
+
const model = modelDisplayName(state);
|
|
315449
|
+
if (model) {
|
|
315450
|
+
const modelLabel = `${model}${state.thinking ? " thinking" : ""}`;
|
|
315451
|
+
let renderedModelLabel = chalk.hex(colors.text)(modelLabel);
|
|
315452
|
+
if (isRainbowDancing()) renderedModelLabel = renderDanceFooterModel(modelLabel);
|
|
315453
|
+
left.push(renderedModelLabel);
|
|
315454
|
+
}
|
|
315455
|
+
if (this.backgroundBashTaskCount > 0) {
|
|
315456
|
+
const noun = this.backgroundBashTaskCount === 1 ? "task" : "tasks";
|
|
315457
|
+
left.push(chalk.hex(colors.primary)(`[${String(this.backgroundBashTaskCount)} ${noun} running]`));
|
|
315458
|
+
}
|
|
315459
|
+
if (this.backgroundAgentCount > 0) {
|
|
315460
|
+
const noun = this.backgroundAgentCount === 1 ? "agent" : "agents";
|
|
315461
|
+
left.push(chalk.hex(colors.primary)(`[${String(this.backgroundAgentCount)} ${noun} running]`));
|
|
315462
|
+
}
|
|
315463
|
+
const cwd = shortenCwd(state.workDir);
|
|
315464
|
+
if (cwd) left.push(chalk.hex(colors.textDim)(cwd));
|
|
315465
|
+
const git = this.gitCache.getStatus();
|
|
315466
|
+
if (git !== null) left.push(formatFooterGitBadge(git, colors));
|
|
315467
|
+
const leftLine = left.join(" ");
|
|
315468
|
+
const leftWidth = visibleWidth(leftLine);
|
|
315469
|
+
const { primary, pair } = tipsForIndex(currentTipIndex());
|
|
315470
|
+
const remaining = Math.max(0, width - leftWidth - 2);
|
|
315471
|
+
let tipText = "";
|
|
315472
|
+
if (pair && visibleWidth(pair) <= remaining) tipText = pair;
|
|
315473
|
+
else if (primary && visibleWidth(primary) <= remaining) tipText = primary;
|
|
315474
|
+
let line1;
|
|
315475
|
+
if (tipText) {
|
|
315476
|
+
const pad = width - leftWidth - visibleWidth(tipText);
|
|
315477
|
+
line1 = leftLine + " ".repeat(Math.max(0, pad)) + chalk.hex(colors.textMuted)(tipText);
|
|
315478
|
+
} else if (leftWidth <= width) line1 = leftLine;
|
|
315479
|
+
else line1 = truncateToWidth(leftLine, width, "…");
|
|
315480
|
+
const contextText = formatContextStatus(state.contextUsage, state.contextTokens, state.maxContextTokens);
|
|
315481
|
+
const contextWidth = visibleWidth(contextText);
|
|
315482
|
+
let line2;
|
|
315483
|
+
if (this.transientHint) {
|
|
315484
|
+
const maxHintWidth = Math.max(0, width - contextWidth - 1);
|
|
315485
|
+
const shownHint = visibleWidth(this.transientHint) <= maxHintWidth ? this.transientHint : truncateToWidth(this.transientHint, maxHintWidth, "…");
|
|
315486
|
+
const hintWidth = visibleWidth(shownHint);
|
|
315487
|
+
const pad = Math.max(0, width - hintWidth - contextWidth);
|
|
315488
|
+
line2 = chalk.hex(colors.warning).bold(shownHint) + " ".repeat(pad) + chalk.hex(colors.text)(contextText);
|
|
315489
|
+
} else {
|
|
315490
|
+
const leftPad = Math.max(0, width - contextWidth);
|
|
315491
|
+
line2 = " ".repeat(leftPad) + chalk.hex(colors.text)(contextText);
|
|
315492
|
+
}
|
|
315493
|
+
return [truncateToWidth(line1, width), truncateToWidth(line2, width)];
|
|
315494
|
+
}
|
|
315495
|
+
syncGoalClock(goal) {
|
|
315496
|
+
const key = goalSnapshotKey(goal);
|
|
315497
|
+
if (key === this.goalSnapshotKey) return;
|
|
315498
|
+
this.goalSnapshotKey = key;
|
|
315499
|
+
this.goalObservedAtMs = Date.now();
|
|
315500
|
+
}
|
|
315501
|
+
syncGoalTimer(goal) {
|
|
315502
|
+
if (goal?.status === "active") {
|
|
315503
|
+
if (this.goalTimer !== null) return;
|
|
315504
|
+
this.goalTimer = setInterval(() => {
|
|
315505
|
+
this.onRefresh();
|
|
315506
|
+
}, GOAL_TIMER_INTERVAL_MS);
|
|
315507
|
+
this.goalTimer.unref?.();
|
|
315508
|
+
return;
|
|
315509
|
+
}
|
|
315510
|
+
if (this.goalTimer !== null) {
|
|
315511
|
+
clearInterval(this.goalTimer);
|
|
315512
|
+
this.goalTimer = null;
|
|
315513
|
+
}
|
|
315514
|
+
}
|
|
315515
|
+
goalWallClockMs(goal) {
|
|
315516
|
+
if (goal === null || goal === void 0) return void 0;
|
|
315517
|
+
if (goal.status !== "active") return goal.wallClockMs;
|
|
315518
|
+
return goal.wallClockMs + Math.max(0, Date.now() - this.goalObservedAtMs);
|
|
315519
|
+
}
|
|
315520
|
+
};
|
|
315521
|
+
function goalSnapshotKey(goal) {
|
|
315522
|
+
if (goal === null || goal === void 0) return null;
|
|
315523
|
+
return [
|
|
315524
|
+
goal.goalId,
|
|
315525
|
+
goal.status,
|
|
315526
|
+
goal.terminalReason ?? "",
|
|
315527
|
+
String(goal.turnsUsed),
|
|
315528
|
+
String(goal.tokensUsed),
|
|
315529
|
+
String(goal.wallClockMs),
|
|
315530
|
+
String(goal.budget.tokenBudget),
|
|
315531
|
+
String(goal.budget.turnBudget),
|
|
315532
|
+
String(goal.budget.wallClockBudgetMs)
|
|
315533
|
+
].join("\0");
|
|
315534
|
+
}
|
|
315535
|
+
//#endregion
|
|
315536
|
+
//#region src/tui/components/chrome/working-tips.ts
|
|
315537
|
+
const TIP_ROTATE_INTERVAL_MS = 1e4;
|
|
315538
|
+
const WORKING_TIP_ROTATION = buildWeightedTips(WORKING_TIPS);
|
|
315539
|
+
function currentWorkingTip(now = Date.now()) {
|
|
315540
|
+
if (WORKING_TIP_ROTATION.length === 0) return void 0;
|
|
315541
|
+
return WORKING_TIP_ROTATION[Math.floor(now / TIP_ROTATE_INTERVAL_MS) % WORKING_TIP_ROTATION.length];
|
|
315542
|
+
}
|
|
315543
|
+
/**
|
|
315544
|
+
* Pick a random tip from the weighted working-tip rotation.
|
|
315545
|
+
* If `excludeText` is provided and there are other tips available, avoid
|
|
315546
|
+
* returning the same text twice in a row.
|
|
315547
|
+
*/
|
|
315548
|
+
function pickRandomWorkingTip(excludeText) {
|
|
315549
|
+
if (WORKING_TIP_ROTATION.length === 0) return void 0;
|
|
315550
|
+
const candidates = excludeText === void 0 || WORKING_TIP_ROTATION.length === 1 ? WORKING_TIP_ROTATION : WORKING_TIP_ROTATION.filter((t) => t.text !== excludeText);
|
|
315551
|
+
const pool = candidates.length > 0 ? candidates : WORKING_TIP_ROTATION;
|
|
315552
|
+
return pool[Math.floor(Math.random() * pool.length)];
|
|
315553
|
+
}
|
|
315554
|
+
//#endregion
|
|
314497
315555
|
//#region src/tui/components/dialogs/approval-panel.ts
|
|
314498
315556
|
/**
|
|
314499
315557
|
* ApprovalPanel — pi-tui version of the approval request UI.
|
|
@@ -314957,6 +316015,10 @@ const DEFAULT_KEYBOARD_SHORTCUTS = [
|
|
|
314957
316015
|
keys: "Ctrl-O",
|
|
314958
316016
|
description: "Toggle tool output expansion"
|
|
314959
316017
|
},
|
|
316018
|
+
{
|
|
316019
|
+
keys: "Ctrl-T",
|
|
316020
|
+
description: "Expand / collapse the todo list (when truncated)"
|
|
316021
|
+
},
|
|
314960
316022
|
{
|
|
314961
316023
|
keys: "Ctrl-S",
|
|
314962
316024
|
description: "Steer — inject a follow-up during streaming"
|
|
@@ -316341,20 +317403,20 @@ var NoticeMessageComponent = class extends Container {
|
|
|
316341
317403
|
//#endregion
|
|
316342
317404
|
//#region src/tui/components/panes/activity-pane.ts
|
|
316343
317405
|
var ActivityPaneComponent = class extends Container {
|
|
317406
|
+
spinnerRef;
|
|
316344
317407
|
constructor(options) {
|
|
316345
317408
|
super();
|
|
316346
|
-
|
|
316347
|
-
|
|
316348
|
-
this.addChild(new Spacer(1));
|
|
316349
|
-
this.addChild(options.spinner);
|
|
316350
|
-
}
|
|
316351
|
-
return;
|
|
316352
|
-
}
|
|
316353
|
-
if (options.mode === "composing" && options.spinner !== void 0) {
|
|
317409
|
+
this.spinnerRef = options.spinner;
|
|
317410
|
+
if ((options.mode === "waiting" || options.mode === "tool" || options.mode === "composing") && options.spinner !== void 0) {
|
|
316354
317411
|
this.addChild(new Spacer(1));
|
|
317412
|
+
if (options.tip) options.spinner.setTip(` · Tip: ${options.tip}`);
|
|
316355
317413
|
this.addChild(options.spinner);
|
|
316356
317414
|
}
|
|
316357
317415
|
}
|
|
317416
|
+
render(width) {
|
|
317417
|
+
if (this.spinnerRef && "setAvailableWidth" in this.spinnerRef) this.spinnerRef.setAvailableWidth(width);
|
|
317418
|
+
return super.render(width);
|
|
317419
|
+
}
|
|
316358
317420
|
};
|
|
316359
317421
|
//#endregion
|
|
316360
317422
|
//#region src/tui/components/panes/queue-pane.ts
|
|
@@ -317191,6 +318253,249 @@ function formatBtwTurnEnd(event) {
|
|
|
317191
318253
|
return `BTW turn ended with reason: ${event.reason}`;
|
|
317192
318254
|
}
|
|
317193
318255
|
//#endregion
|
|
318256
|
+
//#region src/utils/clipboard/clipboard-common.ts
|
|
318257
|
+
const SUPPORTED_IMAGE_MIME_TYPES = [
|
|
318258
|
+
"image/png",
|
|
318259
|
+
"image/jpeg",
|
|
318260
|
+
"image/webp",
|
|
318261
|
+
"image/gif"
|
|
318262
|
+
];
|
|
318263
|
+
const DEFAULT_LIST_TIMEOUT_MS = 1e3;
|
|
318264
|
+
const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
318265
|
+
function baseMimeType(raw) {
|
|
318266
|
+
return raw.split(";")[0]?.trim().toLowerCase() ?? raw.toLowerCase();
|
|
318267
|
+
}
|
|
318268
|
+
function isSupportedImageMimeType(mime) {
|
|
318269
|
+
const base = baseMimeType(mime);
|
|
318270
|
+
return SUPPORTED_IMAGE_MIME_TYPES.includes(base);
|
|
318271
|
+
}
|
|
318272
|
+
function parseTargetList(output) {
|
|
318273
|
+
return output.toString("utf-8").split(/\r?\n/).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
318274
|
+
}
|
|
318275
|
+
function runCommand$1(command, args, options) {
|
|
318276
|
+
const result = spawnSync(command, args, {
|
|
318277
|
+
timeout: options?.timeoutMs ?? 1e3,
|
|
318278
|
+
maxBuffer: DEFAULT_MAX_BUFFER_BYTES,
|
|
318279
|
+
env: options?.env
|
|
318280
|
+
});
|
|
318281
|
+
if (result.error !== void 0 || result.status !== 0) return {
|
|
318282
|
+
ok: false,
|
|
318283
|
+
stdout: Buffer.alloc(0)
|
|
318284
|
+
};
|
|
318285
|
+
return {
|
|
318286
|
+
ok: true,
|
|
318287
|
+
stdout: Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.from(result.stdout ?? "")
|
|
318288
|
+
};
|
|
318289
|
+
}
|
|
318290
|
+
function isWaylandSession(env) {
|
|
318291
|
+
return Boolean(env["WAYLAND_DISPLAY"]) || env["XDG_SESSION_TYPE"] === "wayland";
|
|
318292
|
+
}
|
|
318293
|
+
function isWSL(env) {
|
|
318294
|
+
if (env["WSL_DISTRO_NAME"] !== void 0 || env["WSLENV"] !== void 0) return true;
|
|
318295
|
+
try {
|
|
318296
|
+
return /microsoft|wsl/i.test(readFileSync("/proc/version", "utf-8"));
|
|
318297
|
+
} catch {
|
|
318298
|
+
return false;
|
|
318299
|
+
}
|
|
318300
|
+
}
|
|
318301
|
+
function isFileLikeNativeFormat(format) {
|
|
318302
|
+
const f = format.toLowerCase();
|
|
318303
|
+
const base = baseMimeType(format);
|
|
318304
|
+
return f.includes("file-url") || f.includes("file url") || f.includes("nsfilenames") || f.includes("com.apple.finder") || base === "text/uri-list" || base === "public.url";
|
|
318305
|
+
}
|
|
318306
|
+
function safeAvailableFormats(clip) {
|
|
318307
|
+
if (clip?.availableFormats === void 0) return [];
|
|
318308
|
+
try {
|
|
318309
|
+
return clip.availableFormats();
|
|
318310
|
+
} catch {
|
|
318311
|
+
return [];
|
|
318312
|
+
}
|
|
318313
|
+
}
|
|
318314
|
+
//#endregion
|
|
318315
|
+
//#region src/utils/clipboard/clipboard-has-image.ts
|
|
318316
|
+
const DEFAULT_POWERSHELL_TIMEOUT_MS$1 = 2e3;
|
|
318317
|
+
function hasImageViaWlPaste(run) {
|
|
318318
|
+
const list = run("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
318319
|
+
if (!list.ok) return false;
|
|
318320
|
+
return parseTargetList(list.stdout).some((t) => isSupportedImageMimeType(t));
|
|
318321
|
+
}
|
|
318322
|
+
function hasImageViaXclip(run) {
|
|
318323
|
+
const targets = run("xclip", [
|
|
318324
|
+
"-selection",
|
|
318325
|
+
"clipboard",
|
|
318326
|
+
"-t",
|
|
318327
|
+
"TARGETS",
|
|
318328
|
+
"-o"
|
|
318329
|
+
], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
318330
|
+
if (!targets.ok) return false;
|
|
318331
|
+
return parseTargetList(targets.stdout).some((t) => isSupportedImageMimeType(t));
|
|
318332
|
+
}
|
|
318333
|
+
function hasImageViaPowerShell(run) {
|
|
318334
|
+
const result = run("powershell.exe", [
|
|
318335
|
+
"-NoProfile",
|
|
318336
|
+
"-Command",
|
|
318337
|
+
"Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); ($img -ne $null)"
|
|
318338
|
+
], { timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS$1 });
|
|
318339
|
+
if (!result.ok) return false;
|
|
318340
|
+
return result.stdout.toString("utf-8").trim().toLowerCase() === "true";
|
|
318341
|
+
}
|
|
318342
|
+
async function hasImageViaNative(clip) {
|
|
318343
|
+
if (clip === null) return false;
|
|
318344
|
+
if (safeAvailableFormats(clip).some(isFileLikeNativeFormat)) return false;
|
|
318345
|
+
try {
|
|
318346
|
+
return clip.hasImage();
|
|
318347
|
+
} catch {
|
|
318348
|
+
return false;
|
|
318349
|
+
}
|
|
318350
|
+
}
|
|
318351
|
+
async function clipboardHasImage(options) {
|
|
318352
|
+
const env = options?.env ?? process.env;
|
|
318353
|
+
const platform = options?.platform ?? process.platform;
|
|
318354
|
+
const clip = options?.clipboard ?? clipboard;
|
|
318355
|
+
const run = options?.runCommand ?? runCommand$1;
|
|
318356
|
+
if (env["TERMUX_VERSION"] !== void 0) return false;
|
|
318357
|
+
if (platform === "linux") {
|
|
318358
|
+
const wayland = isWaylandSession(env);
|
|
318359
|
+
const wsl = isWSL(env);
|
|
318360
|
+
let xclipResult;
|
|
318361
|
+
const xclipHasImage = () => {
|
|
318362
|
+
xclipResult ??= hasImageViaXclip(run);
|
|
318363
|
+
return xclipResult;
|
|
318364
|
+
};
|
|
318365
|
+
if (wayland || wsl) {
|
|
318366
|
+
if (hasImageViaWlPaste(run)) return true;
|
|
318367
|
+
if (xclipHasImage()) return true;
|
|
318368
|
+
}
|
|
318369
|
+
if (wsl && hasImageViaPowerShell(run)) return true;
|
|
318370
|
+
if (!wayland) {
|
|
318371
|
+
if (xclipHasImage()) return true;
|
|
318372
|
+
if (await hasImageViaNative(clip)) return true;
|
|
318373
|
+
}
|
|
318374
|
+
return false;
|
|
318375
|
+
}
|
|
318376
|
+
if (platform === "darwin") return hasImageViaNative(clip);
|
|
318377
|
+
if (platform === "win32") return hasImageViaNative(clip);
|
|
318378
|
+
return false;
|
|
318379
|
+
}
|
|
318380
|
+
//#endregion
|
|
318381
|
+
//#region src/tui/constant/clipboard-image-hint.ts
|
|
318382
|
+
const FOCUS_DEBOUNCE_MS = 1e3;
|
|
318383
|
+
const HINT_DISPLAY_MS = 4e3;
|
|
318384
|
+
//#endregion
|
|
318385
|
+
//#region src/tui/utils/terminal-focus.ts
|
|
318386
|
+
function installTerminalFocusTracking(state) {
|
|
318387
|
+
state.terminalState.focused = true;
|
|
318388
|
+
const disposeInputListener = state.ui.addInputListener((data) => handleTerminalFocusInput(state.terminalState, data));
|
|
318389
|
+
state.terminal.write(ENABLE_TERMINAL_FOCUS_REPORTING);
|
|
318390
|
+
return () => {
|
|
318391
|
+
disposeInputListener();
|
|
318392
|
+
state.terminal.write(DISABLE_TERMINAL_FOCUS_REPORTING);
|
|
318393
|
+
state.terminalState.focused = true;
|
|
318394
|
+
};
|
|
318395
|
+
}
|
|
318396
|
+
function handleTerminalFocusInput(state, data) {
|
|
318397
|
+
if (data === TERMINAL_FOCUS_IN) {
|
|
318398
|
+
state.focused = true;
|
|
318399
|
+
return { consume: true };
|
|
318400
|
+
}
|
|
318401
|
+
if (data === TERMINAL_FOCUS_OUT) {
|
|
318402
|
+
state.focused = false;
|
|
318403
|
+
return { consume: true };
|
|
318404
|
+
}
|
|
318405
|
+
}
|
|
318406
|
+
//#endregion
|
|
318407
|
+
//#region src/tui/controllers/clipboard-image-hint.ts
|
|
318408
|
+
function getPasteImageShortcut() {
|
|
318409
|
+
return process.platform === "win32" ? "Alt+V" : "Ctrl+V";
|
|
318410
|
+
}
|
|
318411
|
+
var ClipboardImageHintController = class {
|
|
318412
|
+
host;
|
|
318413
|
+
disposeInputListener;
|
|
318414
|
+
debounceTimer;
|
|
318415
|
+
clearHintTimer;
|
|
318416
|
+
lastHintAtMs = 0;
|
|
318417
|
+
lastHintText;
|
|
318418
|
+
checkGeneration = 0;
|
|
318419
|
+
focused = true;
|
|
318420
|
+
constructor(host) {
|
|
318421
|
+
this.host = host;
|
|
318422
|
+
}
|
|
318423
|
+
start() {
|
|
318424
|
+
this.disposeInputListener = this.host.ui.addInputListener((data) => {
|
|
318425
|
+
this.handleInput(data);
|
|
318426
|
+
});
|
|
318427
|
+
}
|
|
318428
|
+
stop() {
|
|
318429
|
+
this.clearDebounceTimer();
|
|
318430
|
+
this.clearClearHintTimer();
|
|
318431
|
+
this.disposeInputListener?.();
|
|
318432
|
+
this.disposeInputListener = void 0;
|
|
318433
|
+
this.checkGeneration += 1;
|
|
318434
|
+
this.clearOwnedHint();
|
|
318435
|
+
this.lastHintAtMs = 0;
|
|
318436
|
+
}
|
|
318437
|
+
handleInput(data) {
|
|
318438
|
+
if (data === TERMINAL_FOCUS_IN) {
|
|
318439
|
+
this.focused = true;
|
|
318440
|
+
this.scheduleCheck();
|
|
318441
|
+
return;
|
|
318442
|
+
}
|
|
318443
|
+
if (data === TERMINAL_FOCUS_OUT) {
|
|
318444
|
+
this.focused = false;
|
|
318445
|
+
this.clearDebounceTimer();
|
|
318446
|
+
return;
|
|
318447
|
+
}
|
|
318448
|
+
}
|
|
318449
|
+
scheduleCheck() {
|
|
318450
|
+
this.clearDebounceTimer();
|
|
318451
|
+
this.checkGeneration += 1;
|
|
318452
|
+
const generation = this.checkGeneration;
|
|
318453
|
+
this.debounceTimer = setTimeout(() => void this.runCheck(generation), FOCUS_DEBOUNCE_MS);
|
|
318454
|
+
}
|
|
318455
|
+
clearDebounceTimer() {
|
|
318456
|
+
if (this.debounceTimer !== void 0) {
|
|
318457
|
+
clearTimeout(this.debounceTimer);
|
|
318458
|
+
this.debounceTimer = void 0;
|
|
318459
|
+
}
|
|
318460
|
+
}
|
|
318461
|
+
clearClearHintTimer() {
|
|
318462
|
+
if (this.clearHintTimer !== void 0) {
|
|
318463
|
+
clearTimeout(this.clearHintTimer);
|
|
318464
|
+
this.clearHintTimer = void 0;
|
|
318465
|
+
}
|
|
318466
|
+
}
|
|
318467
|
+
clearOwnedHint() {
|
|
318468
|
+
if (this.host.footer.getTransientHint() === this.lastHintText) {
|
|
318469
|
+
this.host.footer.setTransientHint(null);
|
|
318470
|
+
this.host.requestRender();
|
|
318471
|
+
}
|
|
318472
|
+
this.lastHintText = void 0;
|
|
318473
|
+
}
|
|
318474
|
+
async runCheck(generation) {
|
|
318475
|
+
if (!this.focused) return;
|
|
318476
|
+
if (!this.host.getModelSupportsImage()) return;
|
|
318477
|
+
if (Date.now() - this.lastHintAtMs < 3e4) return;
|
|
318478
|
+
let hasImage = false;
|
|
318479
|
+
try {
|
|
318480
|
+
hasImage = await clipboardHasImage();
|
|
318481
|
+
} catch {
|
|
318482
|
+
return;
|
|
318483
|
+
}
|
|
318484
|
+
if (generation !== this.checkGeneration) return;
|
|
318485
|
+
if (!this.focused) return;
|
|
318486
|
+
if (!hasImage) return;
|
|
318487
|
+
const hintText = `Image in clipboard · ${getPasteImageShortcut()} to paste`;
|
|
318488
|
+
this.clearClearHintTimer();
|
|
318489
|
+
this.lastHintText = hintText;
|
|
318490
|
+
this.host.footer.setTransientHint(hintText);
|
|
318491
|
+
this.host.requestRender();
|
|
318492
|
+
this.lastHintAtMs = Date.now();
|
|
318493
|
+
this.clearHintTimer = setTimeout(() => {
|
|
318494
|
+
this.clearOwnedHint();
|
|
318495
|
+
}, HINT_DISPLAY_MS);
|
|
318496
|
+
}
|
|
318497
|
+
};
|
|
318498
|
+
//#endregion
|
|
317194
318499
|
//#region src/utils/image/image-mime.ts
|
|
317195
318500
|
function parseImageMeta(bytes) {
|
|
317196
318501
|
if (isPng(bytes)) return parsePng(bytes);
|
|
@@ -317345,12 +318650,6 @@ var ClipboardMediaError = class extends Error {
|
|
|
317345
318650
|
this.name = "ClipboardMediaError";
|
|
317346
318651
|
}
|
|
317347
318652
|
};
|
|
317348
|
-
const SUPPORTED_IMAGE_MIME_TYPES = [
|
|
317349
|
-
"image/png",
|
|
317350
|
-
"image/jpeg",
|
|
317351
|
-
"image/webp",
|
|
317352
|
-
"image/gif"
|
|
317353
|
-
];
|
|
317354
318653
|
const MAX_VIDEO_BYTES = 100 * 1024 * 1024;
|
|
317355
318654
|
const VIDEO_MIME_BY_SUFFIX = Object.freeze({
|
|
317356
318655
|
".mp4": "video/mp4",
|
|
@@ -317367,10 +318666,8 @@ const VIDEO_MIME_BY_SUFFIX = Object.freeze({
|
|
|
317367
318666
|
".3gp": "video/3gpp",
|
|
317368
318667
|
".3g2": "video/3gpp2"
|
|
317369
318668
|
});
|
|
317370
|
-
const DEFAULT_LIST_TIMEOUT_MS = 1e3;
|
|
317371
318669
|
const DEFAULT_READ_TIMEOUT_MS = 3e3;
|
|
317372
318670
|
const DEFAULT_POWERSHELL_TIMEOUT_MS = 5e3;
|
|
317373
|
-
const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
317374
318671
|
const MACOS_FILE_PATH_SCRIPT = String.raw`
|
|
317375
318672
|
ObjC.import('AppKit');
|
|
317376
318673
|
ObjC.import('Foundation');
|
|
@@ -317405,24 +318702,6 @@ if (String(pb) !== '[id nil]') {
|
|
|
317405
318702
|
}
|
|
317406
318703
|
out.join('\n');
|
|
317407
318704
|
`.trim();
|
|
317408
|
-
function isWaylandSession(env) {
|
|
317409
|
-
return Boolean(env["WAYLAND_DISPLAY"]) || env["XDG_SESSION_TYPE"] === "wayland";
|
|
317410
|
-
}
|
|
317411
|
-
function isWSL(env) {
|
|
317412
|
-
if (env["WSL_DISTRO_NAME"] !== void 0 || env["WSLENV"] !== void 0) return true;
|
|
317413
|
-
try {
|
|
317414
|
-
return /microsoft|wsl/i.test(readFileSync("/proc/version", "utf-8"));
|
|
317415
|
-
} catch {
|
|
317416
|
-
return false;
|
|
317417
|
-
}
|
|
317418
|
-
}
|
|
317419
|
-
function baseMimeType(raw) {
|
|
317420
|
-
return raw.split(";")[0]?.trim().toLowerCase() ?? raw.toLowerCase();
|
|
317421
|
-
}
|
|
317422
|
-
function isSupportedImageMimeType(mime) {
|
|
317423
|
-
const base = baseMimeType(mime);
|
|
317424
|
-
return SUPPORTED_IMAGE_MIME_TYPES.includes(base);
|
|
317425
|
-
}
|
|
317426
318705
|
function selectPreferredImageMimeType(candidates) {
|
|
317427
318706
|
const normalized = candidates.map((t) => t.trim()).filter((t) => t.length > 0).map((raw) => ({
|
|
317428
318707
|
raw,
|
|
@@ -317519,22 +318798,10 @@ function readMediaFromText(text) {
|
|
|
317519
318798
|
return readMediaFromPaths(parseClipboardPaths(text));
|
|
317520
318799
|
}
|
|
317521
318800
|
function runCommand(command, args, options) {
|
|
317522
|
-
|
|
317523
|
-
|
|
317524
|
-
maxBuffer: DEFAULT_MAX_BUFFER_BYTES,
|
|
318801
|
+
return runCommand$1(command, args, {
|
|
318802
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS,
|
|
317525
318803
|
env: options?.env
|
|
317526
318804
|
});
|
|
317527
|
-
if (result.error !== void 0 || result.status !== 0) return {
|
|
317528
|
-
ok: false,
|
|
317529
|
-
stdout: Buffer.alloc(0)
|
|
317530
|
-
};
|
|
317531
|
-
return {
|
|
317532
|
-
ok: true,
|
|
317533
|
-
stdout: Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.from(result.stdout ?? "")
|
|
317534
|
-
};
|
|
317535
|
-
}
|
|
317536
|
-
function parseTargetList(output) {
|
|
317537
|
-
return output.toString("utf-8").split(/\r?\n/).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
317538
318805
|
}
|
|
317539
318806
|
function readClipboardFileMediaViaWlPaste() {
|
|
317540
318807
|
const list = runCommand("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
@@ -317669,19 +318936,6 @@ function readClipboardFilePathsViaMacOs(run) {
|
|
|
317669
318936
|
if (!result.ok || result.stdout.length === 0) return [];
|
|
317670
318937
|
return parseClipboardPaths(result.stdout.toString("utf-8"));
|
|
317671
318938
|
}
|
|
317672
|
-
function isFileLikeNativeFormat(format) {
|
|
317673
|
-
const f = format.toLowerCase();
|
|
317674
|
-
const base = baseMimeType(format);
|
|
317675
|
-
return f.includes("file-url") || f.includes("file url") || f.includes("nsfilenames") || f.includes("com.apple.finder") || base === "text/uri-list" || base === "public.url";
|
|
317676
|
-
}
|
|
317677
|
-
function safeAvailableFormats(clip) {
|
|
317678
|
-
if (clip?.availableFormats === void 0) return [];
|
|
317679
|
-
try {
|
|
317680
|
-
return clip.availableFormats();
|
|
317681
|
-
} catch {
|
|
317682
|
-
return [];
|
|
317683
|
-
}
|
|
317684
|
-
}
|
|
317685
318939
|
async function readClipboardFileMediaViaNativeText(clip) {
|
|
317686
318940
|
if (clip === null) return {
|
|
317687
318941
|
media: null,
|
|
@@ -317910,6 +319164,13 @@ var EditorKeyboardController = class {
|
|
|
317910
319164
|
host.track("shortcut_expand");
|
|
317911
319165
|
host.toggleToolOutputExpansion();
|
|
317912
319166
|
};
|
|
319167
|
+
editor.onToggleTodoExpand = () => {
|
|
319168
|
+
if (!host.state.todoPanel.hasOverflow()) return false;
|
|
319169
|
+
this.clearPendingExit();
|
|
319170
|
+
host.track("shortcut_todo_expand");
|
|
319171
|
+
host.toggleTodoPanelExpansion();
|
|
319172
|
+
return true;
|
|
319173
|
+
};
|
|
317913
319174
|
editor.onCtrlS = () => {
|
|
317914
319175
|
if (host.state.appState.streamingPhase === "idle" || host.state.appState.isCompacting) return;
|
|
317915
319176
|
const text = editor.getText().trim();
|
|
@@ -320773,7 +322034,7 @@ var StreamingUIController = class {
|
|
|
320773
322034
|
this._activeCompactionBlock.markDone();
|
|
320774
322035
|
this._activeCompactionBlock = void 0;
|
|
320775
322036
|
}
|
|
320776
|
-
const block = new CompactionComponent(state.ui, instruction);
|
|
322037
|
+
const block = new CompactionComponent(state.ui, instruction, currentWorkingTip()?.text);
|
|
320777
322038
|
this._activeCompactionBlock = block;
|
|
320778
322039
|
state.transcriptContainer.addChild(block);
|
|
320779
322040
|
state.ui.requestRender();
|
|
@@ -322400,625 +323661,6 @@ function adaptQuestionAnswers(event, response) {
|
|
|
322400
323661
|
} : null;
|
|
322401
323662
|
}
|
|
322402
323663
|
//#endregion
|
|
322403
|
-
//#region src/utils/git/git-status.ts
|
|
322404
|
-
/**
|
|
322405
|
-
* Cached git branch + working-tree status for the footer/statusline.
|
|
322406
|
-
*
|
|
322407
|
-
* Branch name refreshes every 5s, porcelain status every 15s. Branch
|
|
322408
|
-
* and status reads stay synchronous with short timeouts. Pull request
|
|
322409
|
-
* lookup uses an async cache so a slow `gh pr view` never blocks
|
|
322410
|
-
* footer rendering.
|
|
322411
|
-
*/
|
|
322412
|
-
const BRANCH_TTL_MS = 5e3;
|
|
322413
|
-
const STATUS_TTL_MS = 15e3;
|
|
322414
|
-
const PULL_REQUEST_TTL_MS = 6e4;
|
|
322415
|
-
const SPAWN_TIMEOUT_MS = 500;
|
|
322416
|
-
const PR_SPAWN_TIMEOUT_MS = 5e3;
|
|
322417
|
-
const AHEAD_BEHIND_RE = /\[(?:ahead (\d+))?(?:, )?(?:behind (\d+))?\]/;
|
|
322418
|
-
function createGitStatusCache(workDir, options = {}) {
|
|
322419
|
-
const isRepo = detectGitRepo(workDir);
|
|
322420
|
-
let branch = {
|
|
322421
|
-
value: null,
|
|
322422
|
-
fetchedAt: 0
|
|
322423
|
-
};
|
|
322424
|
-
let status = {
|
|
322425
|
-
dirty: false,
|
|
322426
|
-
ahead: 0,
|
|
322427
|
-
behind: 0,
|
|
322428
|
-
diffAdded: 0,
|
|
322429
|
-
diffDeleted: 0,
|
|
322430
|
-
fetchedAt: 0
|
|
322431
|
-
};
|
|
322432
|
-
let pullRequest = {
|
|
322433
|
-
value: null,
|
|
322434
|
-
branch: null,
|
|
322435
|
-
fetchedAt: 0,
|
|
322436
|
-
pendingBranch: null,
|
|
322437
|
-
requestId: 0
|
|
322438
|
-
};
|
|
322439
|
-
return { getStatus: () => {
|
|
322440
|
-
if (!isRepo) return null;
|
|
322441
|
-
const now = Date.now();
|
|
322442
|
-
if (now - branch.fetchedAt >= BRANCH_TTL_MS) branch = {
|
|
322443
|
-
value: readBranch(workDir),
|
|
322444
|
-
fetchedAt: now
|
|
322445
|
-
};
|
|
322446
|
-
if (branch.value === null) return null;
|
|
322447
|
-
if (now - status.fetchedAt >= STATUS_TTL_MS) status = {
|
|
322448
|
-
...readStatus(workDir),
|
|
322449
|
-
fetchedAt: now
|
|
322450
|
-
};
|
|
322451
|
-
refreshPullRequestIfNeeded(branch.value, now);
|
|
322452
|
-
return {
|
|
322453
|
-
branch: branch.value,
|
|
322454
|
-
dirty: status.dirty,
|
|
322455
|
-
ahead: status.ahead,
|
|
322456
|
-
behind: status.behind,
|
|
322457
|
-
diffAdded: status.diffAdded,
|
|
322458
|
-
diffDeleted: status.diffDeleted,
|
|
322459
|
-
pullRequest: pullRequest.branch === branch.value ? pullRequest.value : null
|
|
322460
|
-
};
|
|
322461
|
-
} };
|
|
322462
|
-
function refreshPullRequestIfNeeded(branchName, now) {
|
|
322463
|
-
if (pullRequest.pendingBranch === branchName) return;
|
|
322464
|
-
const fetchedAt = pullRequest.branch === branchName ? pullRequest.fetchedAt : 0;
|
|
322465
|
-
if (now - fetchedAt < PULL_REQUEST_TTL_MS) return;
|
|
322466
|
-
const requestId = pullRequest.requestId + 1;
|
|
322467
|
-
pullRequest = {
|
|
322468
|
-
value: pullRequest.branch === branchName ? pullRequest.value : null,
|
|
322469
|
-
branch: branchName,
|
|
322470
|
-
fetchedAt,
|
|
322471
|
-
pendingBranch: branchName,
|
|
322472
|
-
requestId
|
|
322473
|
-
};
|
|
322474
|
-
readPullRequest(workDir).then((value) => {
|
|
322475
|
-
if (pullRequest.requestId !== requestId) return;
|
|
322476
|
-
const changed = !samePullRequest(pullRequest.branch === branchName ? pullRequest.value : null, value);
|
|
322477
|
-
pullRequest = {
|
|
322478
|
-
value,
|
|
322479
|
-
branch: branchName,
|
|
322480
|
-
fetchedAt: Date.now(),
|
|
322481
|
-
pendingBranch: null,
|
|
322482
|
-
requestId
|
|
322483
|
-
};
|
|
322484
|
-
if (changed) options.onChange?.();
|
|
322485
|
-
});
|
|
322486
|
-
}
|
|
322487
|
-
}
|
|
322488
|
-
function detectGitRepo(workDir) {
|
|
322489
|
-
try {
|
|
322490
|
-
const result = spawnSync("git", [
|
|
322491
|
-
"-C",
|
|
322492
|
-
workDir,
|
|
322493
|
-
"rev-parse",
|
|
322494
|
-
"--is-inside-work-tree"
|
|
322495
|
-
], {
|
|
322496
|
-
encoding: "utf8",
|
|
322497
|
-
timeout: SPAWN_TIMEOUT_MS
|
|
322498
|
-
});
|
|
322499
|
-
return result.status === 0 && result.stdout.trim() === "true";
|
|
322500
|
-
} catch {
|
|
322501
|
-
return false;
|
|
322502
|
-
}
|
|
322503
|
-
}
|
|
322504
|
-
function readBranch(workDir) {
|
|
322505
|
-
try {
|
|
322506
|
-
const result = spawnSync("git", [
|
|
322507
|
-
"-C",
|
|
322508
|
-
workDir,
|
|
322509
|
-
"branch",
|
|
322510
|
-
"--show-current"
|
|
322511
|
-
], {
|
|
322512
|
-
encoding: "utf8",
|
|
322513
|
-
timeout: SPAWN_TIMEOUT_MS
|
|
322514
|
-
});
|
|
322515
|
-
if (result.status !== 0) return null;
|
|
322516
|
-
const name = result.stdout.trim();
|
|
322517
|
-
return name.length > 0 ? name : null;
|
|
322518
|
-
} catch {
|
|
322519
|
-
return null;
|
|
322520
|
-
}
|
|
322521
|
-
}
|
|
322522
|
-
function readStatus(workDir) {
|
|
322523
|
-
try {
|
|
322524
|
-
const result = spawnSync("git", [
|
|
322525
|
-
"-C",
|
|
322526
|
-
workDir,
|
|
322527
|
-
"status",
|
|
322528
|
-
"--porcelain",
|
|
322529
|
-
"-b"
|
|
322530
|
-
], {
|
|
322531
|
-
encoding: "utf8",
|
|
322532
|
-
timeout: SPAWN_TIMEOUT_MS,
|
|
322533
|
-
maxBuffer: 4 * 1024 * 1024
|
|
322534
|
-
});
|
|
322535
|
-
if (result.status !== 0) return {
|
|
322536
|
-
dirty: false,
|
|
322537
|
-
ahead: 0,
|
|
322538
|
-
behind: 0,
|
|
322539
|
-
diffAdded: 0,
|
|
322540
|
-
diffDeleted: 0
|
|
322541
|
-
};
|
|
322542
|
-
let dirty = false;
|
|
322543
|
-
let ahead = 0;
|
|
322544
|
-
let behind = 0;
|
|
322545
|
-
for (const line of result.stdout.split("\n")) if (line.startsWith("## ")) {
|
|
322546
|
-
const m = AHEAD_BEHIND_RE.exec(line);
|
|
322547
|
-
if (m) {
|
|
322548
|
-
ahead = Number.parseInt(m[1] ?? "0", 10) || 0;
|
|
322549
|
-
behind = Number.parseInt(m[2] ?? "0", 10) || 0;
|
|
322550
|
-
}
|
|
322551
|
-
} else if (line.trim().length > 0) dirty = true;
|
|
322552
|
-
const diff = dirty ? readDiffStats(workDir) : {
|
|
322553
|
-
added: 0,
|
|
322554
|
-
deleted: 0
|
|
322555
|
-
};
|
|
322556
|
-
return {
|
|
322557
|
-
dirty,
|
|
322558
|
-
ahead,
|
|
322559
|
-
behind,
|
|
322560
|
-
diffAdded: diff.added,
|
|
322561
|
-
diffDeleted: diff.deleted
|
|
322562
|
-
};
|
|
322563
|
-
} catch {
|
|
322564
|
-
return {
|
|
322565
|
-
dirty: false,
|
|
322566
|
-
ahead: 0,
|
|
322567
|
-
behind: 0,
|
|
322568
|
-
diffAdded: 0,
|
|
322569
|
-
diffDeleted: 0
|
|
322570
|
-
};
|
|
322571
|
-
}
|
|
322572
|
-
}
|
|
322573
|
-
function readDiffStats(workDir) {
|
|
322574
|
-
try {
|
|
322575
|
-
const result = spawnSync("git", [
|
|
322576
|
-
"-C",
|
|
322577
|
-
workDir,
|
|
322578
|
-
"diff",
|
|
322579
|
-
"--numstat",
|
|
322580
|
-
"HEAD",
|
|
322581
|
-
"--"
|
|
322582
|
-
], {
|
|
322583
|
-
encoding: "utf8",
|
|
322584
|
-
timeout: SPAWN_TIMEOUT_MS,
|
|
322585
|
-
maxBuffer: 4 * 1024 * 1024
|
|
322586
|
-
});
|
|
322587
|
-
if (result.status !== 0) return {
|
|
322588
|
-
added: 0,
|
|
322589
|
-
deleted: 0
|
|
322590
|
-
};
|
|
322591
|
-
let added = 0;
|
|
322592
|
-
let deleted = 0;
|
|
322593
|
-
for (const line of result.stdout.split("\n")) {
|
|
322594
|
-
if (!line) continue;
|
|
322595
|
-
const [addedText, deletedText] = line.split(" ");
|
|
322596
|
-
added += parseDiffNumstatCount(addedText);
|
|
322597
|
-
deleted += parseDiffNumstatCount(deletedText);
|
|
322598
|
-
}
|
|
322599
|
-
return {
|
|
322600
|
-
added,
|
|
322601
|
-
deleted
|
|
322602
|
-
};
|
|
322603
|
-
} catch {
|
|
322604
|
-
return {
|
|
322605
|
-
added: 0,
|
|
322606
|
-
deleted: 0
|
|
322607
|
-
};
|
|
322608
|
-
}
|
|
322609
|
-
}
|
|
322610
|
-
function parseDiffNumstatCount(value) {
|
|
322611
|
-
if (value === void 0 || value === "-") return 0;
|
|
322612
|
-
const n = Number.parseInt(value, 10);
|
|
322613
|
-
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
322614
|
-
}
|
|
322615
|
-
function readPullRequest(workDir) {
|
|
322616
|
-
return new Promise((resolve) => {
|
|
322617
|
-
try {
|
|
322618
|
-
execFile("gh", [
|
|
322619
|
-
"pr",
|
|
322620
|
-
"view",
|
|
322621
|
-
"--json",
|
|
322622
|
-
"number,url"
|
|
322623
|
-
], {
|
|
322624
|
-
cwd: workDir,
|
|
322625
|
-
encoding: "utf8",
|
|
322626
|
-
env: {
|
|
322627
|
-
...process.env,
|
|
322628
|
-
GH_NO_UPDATE_NOTIFIER: "1",
|
|
322629
|
-
GH_PROMPT_DISABLED: "1"
|
|
322630
|
-
},
|
|
322631
|
-
timeout: PR_SPAWN_TIMEOUT_MS,
|
|
322632
|
-
maxBuffer: 256 * 1024
|
|
322633
|
-
}, (error, stdout) => {
|
|
322634
|
-
if (error !== null) {
|
|
322635
|
-
resolve(null);
|
|
322636
|
-
return;
|
|
322637
|
-
}
|
|
322638
|
-
resolve(parsePullRequest(stdout));
|
|
322639
|
-
});
|
|
322640
|
-
} catch {
|
|
322641
|
-
resolve(null);
|
|
322642
|
-
}
|
|
322643
|
-
});
|
|
322644
|
-
}
|
|
322645
|
-
function samePullRequest(a, b) {
|
|
322646
|
-
if (a === null || b === null) return a === b;
|
|
322647
|
-
return a.number === b.number && a.url === b.url;
|
|
322648
|
-
}
|
|
322649
|
-
function parsePullRequest(stdout) {
|
|
322650
|
-
try {
|
|
322651
|
-
const raw = JSON.parse(stdout);
|
|
322652
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
322653
|
-
const record = raw;
|
|
322654
|
-
const number = record["number"];
|
|
322655
|
-
const url = record["url"];
|
|
322656
|
-
if (typeof number !== "number" || !Number.isInteger(number) || number <= 0) return null;
|
|
322657
|
-
if (typeof url !== "string" || !isSafeHttpUrl(url)) return null;
|
|
322658
|
-
return {
|
|
322659
|
-
number,
|
|
322660
|
-
url
|
|
322661
|
-
};
|
|
322662
|
-
} catch {
|
|
322663
|
-
return null;
|
|
322664
|
-
}
|
|
322665
|
-
}
|
|
322666
|
-
function isSafeHttpUrl(value) {
|
|
322667
|
-
if (hasControlChars(value)) return false;
|
|
322668
|
-
try {
|
|
322669
|
-
const url = new URL(value);
|
|
322670
|
-
return url.protocol === "https:" || url.protocol === "http:";
|
|
322671
|
-
} catch {
|
|
322672
|
-
return false;
|
|
322673
|
-
}
|
|
322674
|
-
}
|
|
322675
|
-
function hasControlChars(value) {
|
|
322676
|
-
for (const char of value) {
|
|
322677
|
-
const code = char.codePointAt(0) ?? 0;
|
|
322678
|
-
if (code <= 31 || code === 127) return true;
|
|
322679
|
-
}
|
|
322680
|
-
return false;
|
|
322681
|
-
}
|
|
322682
|
-
function formatGitBadgeBase(status) {
|
|
322683
|
-
const parts = [];
|
|
322684
|
-
const diff = formatDiffStats(status);
|
|
322685
|
-
if (diff) parts.push(diff);
|
|
322686
|
-
let sync = "";
|
|
322687
|
-
if (status.ahead > 0) sync += `↑${status.ahead}`;
|
|
322688
|
-
if (status.behind > 0) sync += `↓${status.behind}`;
|
|
322689
|
-
if (sync) parts.push(sync);
|
|
322690
|
-
return parts.length === 0 ? status.branch : `${status.branch} [${parts.join(" ")}]`;
|
|
322691
|
-
}
|
|
322692
|
-
function formatPullRequestBadge(pullRequest, options = {}) {
|
|
322693
|
-
const prText = `[PR#${String(pullRequest.number)}]`;
|
|
322694
|
-
return options.linkPullRequest ? toTerminalHyperlink(prText, pullRequest.url) : prText;
|
|
322695
|
-
}
|
|
322696
|
-
function formatDiffStats(status) {
|
|
322697
|
-
const parts = [];
|
|
322698
|
-
if (status.diffAdded > 0) parts.push(`+${String(status.diffAdded)}`);
|
|
322699
|
-
if (status.diffDeleted > 0) parts.push(`-${String(status.diffDeleted)}`);
|
|
322700
|
-
if (parts.length > 0) return parts.join(" ");
|
|
322701
|
-
return status.dirty ? "±" : null;
|
|
322702
|
-
}
|
|
322703
|
-
function toTerminalHyperlink(text, url) {
|
|
322704
|
-
if (!isSafeHttpUrl(url)) return text;
|
|
322705
|
-
return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
|
|
322706
|
-
}
|
|
322707
|
-
//#endregion
|
|
322708
|
-
//#region src/tui/components/chrome/footer.ts
|
|
322709
|
-
const MAX_CWD_SEGMENTS = 3;
|
|
322710
|
-
const GOAL_TIMER_INTERVAL_MS = 1e3;
|
|
322711
|
-
const TIP_ROTATE_INTERVAL_MS = 1e4;
|
|
322712
|
-
const TIP_SEPARATOR = " | ";
|
|
322713
|
-
const TOOLBAR_TIPS = [
|
|
322714
|
-
{ text: "shift+tab: plan mode" },
|
|
322715
|
-
{ text: "/model: switch model" },
|
|
322716
|
-
{
|
|
322717
|
-
text: "ctrl+s: steer mid-turn",
|
|
322718
|
-
priority: 2
|
|
322719
|
-
},
|
|
322720
|
-
{
|
|
322721
|
-
text: "ctrl+b: background task",
|
|
322722
|
-
priority: 2
|
|
322723
|
-
},
|
|
322724
|
-
{
|
|
322725
|
-
text: "/compact: compact context",
|
|
322726
|
-
priority: 2
|
|
322727
|
-
},
|
|
322728
|
-
{ text: "ctrl+o: expand tool output" },
|
|
322729
|
-
{ text: "/tasks: background tasks" },
|
|
322730
|
-
{ text: "shift+enter: newline" },
|
|
322731
|
-
{
|
|
322732
|
-
text: "/init: generate AGENTS.md",
|
|
322733
|
-
priority: 2
|
|
322734
|
-
},
|
|
322735
|
-
{ text: "@: mention files" },
|
|
322736
|
-
{ text: "ctrl+c: cancel" },
|
|
322737
|
-
{ text: "/theme: switch theme" },
|
|
322738
|
-
{ text: "/auto: auto permission mode" },
|
|
322739
|
-
{ text: "/yolo: toggle yolo" },
|
|
322740
|
-
{ text: "/help: show commands" },
|
|
322741
|
-
{ text: "/dance: rainbow mode, because why not" },
|
|
322742
|
-
{
|
|
322743
|
-
text: "/plugins: manage plugins — try the \"superpowers\" plugin",
|
|
322744
|
-
solo: true,
|
|
322745
|
-
priority: 3
|
|
322746
|
-
},
|
|
322747
|
-
{
|
|
322748
|
-
text: "ask Kimi to schedule tasks, e.g. \"remind me at 5pm\"",
|
|
322749
|
-
solo: true,
|
|
322750
|
-
priority: 3
|
|
322751
|
-
}
|
|
322752
|
-
];
|
|
322753
|
-
/**
|
|
322754
|
-
* Expand tips into a rotation sequence using smooth weighted round-robin
|
|
322755
|
-
* (the nginx SWRR algorithm). Higher-`priority` tips appear more often while
|
|
322756
|
-
* staying evenly spread, so a tip generally does not land next to its own
|
|
322757
|
-
* duplicate. Deterministic and computed once at module load. Exported for
|
|
322758
|
-
* unit testing.
|
|
322759
|
-
*/
|
|
322760
|
-
function buildWeightedTips(tips) {
|
|
322761
|
-
const items = tips.map((t) => ({
|
|
322762
|
-
tip: t,
|
|
322763
|
-
weight: Math.max(1, Math.trunc(t.priority ?? 1)),
|
|
322764
|
-
current: 0
|
|
322765
|
-
}));
|
|
322766
|
-
const total = items.reduce((sum, it) => sum + it.weight, 0);
|
|
322767
|
-
const seq = [];
|
|
322768
|
-
for (let n = 0; n < total; n++) {
|
|
322769
|
-
let best = items[0];
|
|
322770
|
-
for (const it of items) {
|
|
322771
|
-
it.current += it.weight;
|
|
322772
|
-
if (it.current > best.current) best = it;
|
|
322773
|
-
}
|
|
322774
|
-
best.current -= total;
|
|
322775
|
-
seq.push(best.tip);
|
|
322776
|
-
}
|
|
322777
|
-
return seq;
|
|
322778
|
-
}
|
|
322779
|
-
const ROTATION = buildWeightedTips(TOOLBAR_TIPS);
|
|
322780
|
-
function currentTipIndex() {
|
|
322781
|
-
return Math.floor(Date.now() / TIP_ROTATE_INTERVAL_MS);
|
|
322782
|
-
}
|
|
322783
|
-
/**
|
|
322784
|
-
* Pick the tip(s) for a rotation index over the weighted ROTATION sequence.
|
|
322785
|
-
* `primary` is always shown when it fits; `pair` (primary + next tip joined
|
|
322786
|
-
* by the separator) is offered for wide terminals. Pairing is skipped when
|
|
322787
|
-
* the current/next tip is `solo` or when the neighbour is a duplicate of the
|
|
322788
|
-
* current tip (which can happen at the wrap boundary), keeping long/important
|
|
322789
|
-
* tips on their own and avoiding "X | X".
|
|
322790
|
-
*/
|
|
322791
|
-
function tipsForIndex(index) {
|
|
322792
|
-
const n = ROTATION.length;
|
|
322793
|
-
if (n === 0) return {
|
|
322794
|
-
primary: "",
|
|
322795
|
-
pair: null
|
|
322796
|
-
};
|
|
322797
|
-
const offset = (index % n + n) % n;
|
|
322798
|
-
const current = ROTATION[offset];
|
|
322799
|
-
if (n === 1 || current.solo) return {
|
|
322800
|
-
primary: current.text,
|
|
322801
|
-
pair: null
|
|
322802
|
-
};
|
|
322803
|
-
const next = ROTATION[(offset + 1) % n];
|
|
322804
|
-
if (next.solo || next.text === current.text) return {
|
|
322805
|
-
primary: current.text,
|
|
322806
|
-
pair: null
|
|
322807
|
-
};
|
|
322808
|
-
return {
|
|
322809
|
-
primary: current.text,
|
|
322810
|
-
pair: current.text + TIP_SEPARATOR + next.text
|
|
322811
|
-
};
|
|
322812
|
-
}
|
|
322813
|
-
/**
|
|
322814
|
-
* Footer goal badge, e.g. `[goal ● active · 4m · 7 turns]`. Only shown for a
|
|
322815
|
-
* live (active/paused) goal; terminal/no goal -> no badge. Turn count is a raw
|
|
322816
|
-
* count unless an explicit turn budget is set, in which case it shows used/limit.
|
|
322817
|
-
*/
|
|
322818
|
-
function formatGoalBadge(goal, colors, wallClockMs) {
|
|
322819
|
-
if (goal === null || goal === void 0) return null;
|
|
322820
|
-
if (goal.status !== "active" && goal.status !== "paused" && goal.status !== "blocked") return null;
|
|
322821
|
-
const dotColor = goal.status === "active" ? colors.primary : goal.status === "blocked" ? colors.warning : colors.textMuted;
|
|
322822
|
-
const turns = goal.budget.turnBudget !== null ? `${goal.turnsUsed}/${goal.budget.turnBudget} turns` : `${goal.turnsUsed} ${goal.turnsUsed === 1 ? "turn" : "turns"}`;
|
|
322823
|
-
const label = `${goal.status} · ${formatBadgeElapsed(wallClockMs ?? goal.wallClockMs)} · ${turns}`;
|
|
322824
|
-
return chalk.hex(colors.textMuted)("[goal ") + chalk.hex(dotColor)("●") + chalk.hex(colors.textMuted)(` ${label}]`);
|
|
322825
|
-
}
|
|
322826
|
-
function formatBadgeElapsed(ms) {
|
|
322827
|
-
const totalSeconds = Math.round(ms / 1e3);
|
|
322828
|
-
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
322829
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
322830
|
-
if (minutes < 60) return `${minutes}m`;
|
|
322831
|
-
return `${Math.floor(minutes / 60)}h${minutes % 60}m`;
|
|
322832
|
-
}
|
|
322833
|
-
function modelDisplayName(state) {
|
|
322834
|
-
const model = state.availableModels[state.model];
|
|
322835
|
-
return model?.displayName ?? model?.model ?? state.model;
|
|
322836
|
-
}
|
|
322837
|
-
function shortenCwd(path) {
|
|
322838
|
-
if (!path) return path;
|
|
322839
|
-
const home = process.env["HOME"] ?? "";
|
|
322840
|
-
let work = path;
|
|
322841
|
-
if (home && path === home) return "~";
|
|
322842
|
-
if (home && path.startsWith(home + "/")) work = "~" + path.slice(home.length);
|
|
322843
|
-
const segments = work.split("/").filter((s) => s.length > 0);
|
|
322844
|
-
if (segments.length <= MAX_CWD_SEGMENTS) return work;
|
|
322845
|
-
return `…/${segments.slice(-MAX_CWD_SEGMENTS).join("/")}`;
|
|
322846
|
-
}
|
|
322847
|
-
function formatTokenCount(n) {
|
|
322848
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
322849
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
322850
|
-
return String(n);
|
|
322851
|
-
}
|
|
322852
|
-
function safeUsage(usage) {
|
|
322853
|
-
return safeUsageRatio(usage);
|
|
322854
|
-
}
|
|
322855
|
-
function formatContextStatus(usage, tokens, maxTokens) {
|
|
322856
|
-
const pct = `${(safeUsage(usage) * 100).toFixed(1)}%`;
|
|
322857
|
-
if (maxTokens && maxTokens > 0 && tokens !== void 0) return `context: ${pct} (${formatTokenCount(tokens)}/${formatTokenCount(maxTokens)})`;
|
|
322858
|
-
return `context: ${pct}`;
|
|
322859
|
-
}
|
|
322860
|
-
function formatFooterGitBadge(status, colors) {
|
|
322861
|
-
const base = chalk.hex(colors.textDim)(formatGitBadgeBase(status));
|
|
322862
|
-
if (status.pullRequest === null) return base;
|
|
322863
|
-
return `${base} ${chalk.hex(colors.primary)(formatPullRequestBadge(status.pullRequest, { linkPullRequest: true }))}`;
|
|
322864
|
-
}
|
|
322865
|
-
var FooterComponent = class {
|
|
322866
|
-
state;
|
|
322867
|
-
onRefresh;
|
|
322868
|
-
gitCache;
|
|
322869
|
-
gitCacheWorkDir;
|
|
322870
|
-
transientHint = null;
|
|
322871
|
-
goalSnapshotKey = null;
|
|
322872
|
-
goalObservedAtMs = Date.now();
|
|
322873
|
-
goalTimer = null;
|
|
322874
|
-
/**
|
|
322875
|
-
* Non-terminal background-task counts split by kind so the footer can
|
|
322876
|
-
* render two distinct badges. `bashTasks` covers `bash-*` BPM tasks
|
|
322877
|
-
* spawned via `Shell run_in_background=true`; `agentTasks` covers
|
|
322878
|
-
* `agent-*` BPM tasks (background subagents). Either zero hides its
|
|
322879
|
-
* respective badge.
|
|
322880
|
-
*/
|
|
322881
|
-
backgroundBashTaskCount = 0;
|
|
322882
|
-
backgroundAgentCount = 0;
|
|
322883
|
-
constructor(state, onRefresh = () => {}) {
|
|
322884
|
-
this.state = state;
|
|
322885
|
-
this.onRefresh = onRefresh;
|
|
322886
|
-
this.gitCacheWorkDir = state.workDir;
|
|
322887
|
-
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onRefresh });
|
|
322888
|
-
this.syncGoalClock(state.goal);
|
|
322889
|
-
this.syncGoalTimer(state.goal);
|
|
322890
|
-
}
|
|
322891
|
-
setState(state) {
|
|
322892
|
-
if (state.workDir !== this.gitCacheWorkDir) {
|
|
322893
|
-
this.gitCacheWorkDir = state.workDir;
|
|
322894
|
-
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onRefresh });
|
|
322895
|
-
}
|
|
322896
|
-
this.syncGoalClock(state.goal);
|
|
322897
|
-
this.syncGoalTimer(state.goal);
|
|
322898
|
-
this.state = state;
|
|
322899
|
-
}
|
|
322900
|
-
/**
|
|
322901
|
-
* Short-lived hint that replaces the rotating toolbar tips on line 1.
|
|
322902
|
-
* Used by the exit-confirmation double-tap flow to show "Press Ctrl+C
|
|
322903
|
-
* again to exit" without requiring a toast/overlay subsystem.
|
|
322904
|
-
* Pass `null` to clear.
|
|
322905
|
-
*/
|
|
322906
|
-
setTransientHint(hint) {
|
|
322907
|
-
this.transientHint = hint;
|
|
322908
|
-
}
|
|
322909
|
-
getTransientHint() {
|
|
322910
|
-
return this.transientHint;
|
|
322911
|
-
}
|
|
322912
|
-
/**
|
|
322913
|
-
* Sync both background-task badges with live counts. Each non-zero
|
|
322914
|
-
* count produces its own bracketed badge on line 1; zeros hide them
|
|
322915
|
-
* independently.
|
|
322916
|
-
*/
|
|
322917
|
-
setBackgroundCounts(counts) {
|
|
322918
|
-
this.backgroundBashTaskCount = Math.max(0, counts.bashTasks);
|
|
322919
|
-
this.backgroundAgentCount = Math.max(0, counts.agentTasks);
|
|
322920
|
-
}
|
|
322921
|
-
invalidate() {}
|
|
322922
|
-
render(width) {
|
|
322923
|
-
const colors = currentTheme.palette;
|
|
322924
|
-
const state = this.state;
|
|
322925
|
-
const left = [];
|
|
322926
|
-
const modes = [];
|
|
322927
|
-
if (state.permissionMode === "auto") modes.push(chalk.hex(colors.warning).bold("auto"));
|
|
322928
|
-
if (state.permissionMode === "yolo") modes.push(chalk.hex(colors.warning).bold("yolo"));
|
|
322929
|
-
if (state.planMode) modes.push(chalk.hex(colors.primary).bold("plan"));
|
|
322930
|
-
if (state.swarmMode) modes.push(chalk.hex(colors.accent).bold("swarm"));
|
|
322931
|
-
if (modes.length > 0) left.push(modes.join(" "));
|
|
322932
|
-
const goalBadge = formatGoalBadge(state.goal, colors, this.goalWallClockMs(state.goal));
|
|
322933
|
-
if (goalBadge !== null) left.push(goalBadge);
|
|
322934
|
-
const model = modelDisplayName(state);
|
|
322935
|
-
if (model) {
|
|
322936
|
-
const modelLabel = `${model}${state.thinking ? " thinking" : ""}`;
|
|
322937
|
-
let renderedModelLabel = chalk.hex(colors.text)(modelLabel);
|
|
322938
|
-
if (isRainbowDancing()) renderedModelLabel = renderDanceFooterModel(modelLabel);
|
|
322939
|
-
left.push(renderedModelLabel);
|
|
322940
|
-
}
|
|
322941
|
-
if (this.backgroundBashTaskCount > 0) {
|
|
322942
|
-
const noun = this.backgroundBashTaskCount === 1 ? "task" : "tasks";
|
|
322943
|
-
left.push(chalk.hex(colors.primary)(`[${String(this.backgroundBashTaskCount)} ${noun} running]`));
|
|
322944
|
-
}
|
|
322945
|
-
if (this.backgroundAgentCount > 0) {
|
|
322946
|
-
const noun = this.backgroundAgentCount === 1 ? "agent" : "agents";
|
|
322947
|
-
left.push(chalk.hex(colors.primary)(`[${String(this.backgroundAgentCount)} ${noun} running]`));
|
|
322948
|
-
}
|
|
322949
|
-
const cwd = shortenCwd(state.workDir);
|
|
322950
|
-
if (cwd) left.push(chalk.hex(colors.textDim)(cwd));
|
|
322951
|
-
const git = this.gitCache.getStatus();
|
|
322952
|
-
if (git !== null) left.push(formatFooterGitBadge(git, colors));
|
|
322953
|
-
const leftLine = left.join(" ");
|
|
322954
|
-
const leftWidth = visibleWidth(leftLine);
|
|
322955
|
-
const { primary, pair } = tipsForIndex(currentTipIndex());
|
|
322956
|
-
const remaining = Math.max(0, width - leftWidth - 2);
|
|
322957
|
-
let tipText = "";
|
|
322958
|
-
if (pair && visibleWidth(pair) <= remaining) tipText = pair;
|
|
322959
|
-
else if (primary && visibleWidth(primary) <= remaining) tipText = primary;
|
|
322960
|
-
let line1;
|
|
322961
|
-
if (tipText) {
|
|
322962
|
-
const pad = width - leftWidth - visibleWidth(tipText);
|
|
322963
|
-
line1 = leftLine + " ".repeat(Math.max(0, pad)) + chalk.hex(colors.textMuted)(tipText);
|
|
322964
|
-
} else if (leftWidth <= width) line1 = leftLine;
|
|
322965
|
-
else line1 = truncateToWidth(leftLine, width, "…");
|
|
322966
|
-
const contextText = formatContextStatus(state.contextUsage, state.contextTokens, state.maxContextTokens);
|
|
322967
|
-
const contextWidth = visibleWidth(contextText);
|
|
322968
|
-
let line2;
|
|
322969
|
-
if (this.transientHint) {
|
|
322970
|
-
const maxHintWidth = Math.max(0, width - contextWidth - 1);
|
|
322971
|
-
const shownHint = visibleWidth(this.transientHint) <= maxHintWidth ? this.transientHint : truncateToWidth(this.transientHint, maxHintWidth, "…");
|
|
322972
|
-
const hintWidth = visibleWidth(shownHint);
|
|
322973
|
-
const pad = Math.max(0, width - hintWidth - contextWidth);
|
|
322974
|
-
line2 = chalk.hex(colors.warning).bold(shownHint) + " ".repeat(pad) + chalk.hex(colors.text)(contextText);
|
|
322975
|
-
} else {
|
|
322976
|
-
const leftPad = Math.max(0, width - contextWidth);
|
|
322977
|
-
line2 = " ".repeat(leftPad) + chalk.hex(colors.text)(contextText);
|
|
322978
|
-
}
|
|
322979
|
-
return [truncateToWidth(line1, width), truncateToWidth(line2, width)];
|
|
322980
|
-
}
|
|
322981
|
-
syncGoalClock(goal) {
|
|
322982
|
-
const key = goalSnapshotKey(goal);
|
|
322983
|
-
if (key === this.goalSnapshotKey) return;
|
|
322984
|
-
this.goalSnapshotKey = key;
|
|
322985
|
-
this.goalObservedAtMs = Date.now();
|
|
322986
|
-
}
|
|
322987
|
-
syncGoalTimer(goal) {
|
|
322988
|
-
if (goal?.status === "active") {
|
|
322989
|
-
if (this.goalTimer !== null) return;
|
|
322990
|
-
this.goalTimer = setInterval(() => {
|
|
322991
|
-
this.onRefresh();
|
|
322992
|
-
}, GOAL_TIMER_INTERVAL_MS);
|
|
322993
|
-
this.goalTimer.unref?.();
|
|
322994
|
-
return;
|
|
322995
|
-
}
|
|
322996
|
-
if (this.goalTimer !== null) {
|
|
322997
|
-
clearInterval(this.goalTimer);
|
|
322998
|
-
this.goalTimer = null;
|
|
322999
|
-
}
|
|
323000
|
-
}
|
|
323001
|
-
goalWallClockMs(goal) {
|
|
323002
|
-
if (goal === null || goal === void 0) return void 0;
|
|
323003
|
-
if (goal.status !== "active") return goal.wallClockMs;
|
|
323004
|
-
return goal.wallClockMs + Math.max(0, Date.now() - this.goalObservedAtMs);
|
|
323005
|
-
}
|
|
323006
|
-
};
|
|
323007
|
-
function goalSnapshotKey(goal) {
|
|
323008
|
-
if (goal === null || goal === void 0) return null;
|
|
323009
|
-
return [
|
|
323010
|
-
goal.goalId,
|
|
323011
|
-
goal.status,
|
|
323012
|
-
goal.terminalReason ?? "",
|
|
323013
|
-
String(goal.turnsUsed),
|
|
323014
|
-
String(goal.tokensUsed),
|
|
323015
|
-
String(goal.wallClockMs),
|
|
323016
|
-
String(goal.budget.tokenBudget),
|
|
323017
|
-
String(goal.budget.turnBudget),
|
|
323018
|
-
String(goal.budget.wallClockBudgetMs)
|
|
323019
|
-
].join("\0");
|
|
323020
|
-
}
|
|
323021
|
-
//#endregion
|
|
323022
323664
|
//#region src/tui/components/chrome/todo-panel.ts
|
|
323023
323665
|
const MAX_VISIBLE = 5;
|
|
323024
323666
|
/**
|
|
@@ -323079,6 +323721,7 @@ function selectVisibleTodos(todos) {
|
|
|
323079
323721
|
}
|
|
323080
323722
|
var TodoPanelComponent = class {
|
|
323081
323723
|
todos = [];
|
|
323724
|
+
expanded = false;
|
|
323082
323725
|
setTodos(todos) {
|
|
323083
323726
|
this.todos = todos.map((t) => ({
|
|
323084
323727
|
title: t.title,
|
|
@@ -323090,18 +323733,34 @@ var TodoPanelComponent = class {
|
|
|
323090
323733
|
}
|
|
323091
323734
|
clear() {
|
|
323092
323735
|
this.todos = [];
|
|
323736
|
+
this.expanded = false;
|
|
323093
323737
|
}
|
|
323094
323738
|
isEmpty() {
|
|
323095
323739
|
return this.todos.length === 0;
|
|
323096
323740
|
}
|
|
323741
|
+
/** True when the list exceeds the collapsed cap, i.e. there is something to expand. */
|
|
323742
|
+
hasOverflow() {
|
|
323743
|
+
return this.todos.length > MAX_VISIBLE;
|
|
323744
|
+
}
|
|
323745
|
+
setExpanded(expanded) {
|
|
323746
|
+
this.expanded = expanded;
|
|
323747
|
+
}
|
|
323748
|
+
toggleExpanded() {
|
|
323749
|
+
this.expanded = !this.expanded;
|
|
323750
|
+
}
|
|
323097
323751
|
invalidate() {}
|
|
323098
323752
|
render(width) {
|
|
323099
323753
|
if (this.todos.length === 0) return [];
|
|
323100
323754
|
const c = currentTheme.palette;
|
|
323101
|
-
const { rows, hidden } = selectVisibleTodos(this.todos);
|
|
323102
323755
|
const lines = [chalk.hex(c.border)("─".repeat(width)), chalk.hex(c.primary).bold(" Todo")];
|
|
323103
|
-
|
|
323104
|
-
|
|
323756
|
+
if (this.expanded) {
|
|
323757
|
+
for (const todo of this.todos) lines.push(renderRow(todo, c));
|
|
323758
|
+
if (this.todos.length > MAX_VISIBLE) lines.push(chalk.hex(c.textDim)(` all ${String(this.todos.length)} items · ctrl+t to collapse`));
|
|
323759
|
+
} else {
|
|
323760
|
+
const { rows, hidden } = selectVisibleTodos(this.todos);
|
|
323761
|
+
for (const todo of rows) lines.push(renderRow(todo, c));
|
|
323762
|
+
if (hidden > 0) lines.push(chalk.hex(c.textDim)(` … +${hidden} more · ctrl+t to expand`));
|
|
323763
|
+
}
|
|
323105
323764
|
return lines.map((line) => truncateToWidth(line, width));
|
|
323106
323765
|
}
|
|
323107
323766
|
};
|
|
@@ -323302,6 +323961,8 @@ var CustomEditor = class extends Editor {
|
|
|
323302
323961
|
onCtrlS;
|
|
323303
323962
|
/** Return `true` to consume Ctrl+B; return `false`/`undefined` to fall through to the editor default (cursor-left). */
|
|
323304
323963
|
onCtrlB;
|
|
323964
|
+
/** Return `true` to consume Ctrl+T (the todo list had overflow to toggle); return `false`/`undefined` to fall through to the editor default. */
|
|
323965
|
+
onToggleTodoExpand;
|
|
323305
323966
|
onUndo;
|
|
323306
323967
|
onInsertNewline;
|
|
323307
323968
|
onTextPaste;
|
|
@@ -323454,6 +324115,9 @@ var CustomEditor = class extends Editor {
|
|
|
323454
324115
|
if (matchesKey(normalized, Key.ctrl("b"))) {
|
|
323455
324116
|
if (this.onCtrlB?.() === true) return;
|
|
323456
324117
|
}
|
|
324118
|
+
if (matchesKey(normalized, Key.ctrl("t"))) {
|
|
324119
|
+
if (this.onToggleTodoExpand?.() === true) return;
|
|
324120
|
+
}
|
|
323457
324121
|
if (matchesKey(normalized, "shift+tab")) {
|
|
323458
324122
|
this.onShiftTab?.();
|
|
323459
324123
|
return;
|
|
@@ -323483,21 +324147,29 @@ var CustomEditor = class extends Editor {
|
|
|
323483
324147
|
this.onEscape?.();
|
|
323484
324148
|
return;
|
|
323485
324149
|
}
|
|
324150
|
+
if (matchesKey(normalized, Key.tab) && !this.isShowingAutocomplete()) return;
|
|
323486
324151
|
super.handleInput(normalized);
|
|
323487
|
-
this.
|
|
324152
|
+
this.reopenAutocompleteAfterInput();
|
|
323488
324153
|
}
|
|
323489
|
-
|
|
324154
|
+
reopenAutocompleteAfterInput() {
|
|
324155
|
+
if (this.isShowingAutocomplete()) return;
|
|
323490
324156
|
const { line, col } = this.getCursor();
|
|
323491
324157
|
const textBeforeCursor = this.getLines()[line]?.slice(0, col) ?? "";
|
|
323492
|
-
|
|
323493
|
-
if (
|
|
323494
|
-
const
|
|
323495
|
-
|
|
323496
|
-
|
|
323497
|
-
|
|
323498
|
-
|
|
323499
|
-
|
|
323500
|
-
|
|
324158
|
+
const editor = this;
|
|
324159
|
+
if (editor.requestAutocomplete === void 0) return;
|
|
324160
|
+
const trigger = () => {
|
|
324161
|
+
editor.requestAutocomplete?.({
|
|
324162
|
+
force: false,
|
|
324163
|
+
explicitTab: false
|
|
324164
|
+
});
|
|
324165
|
+
};
|
|
324166
|
+
if (textBeforeCursor.endsWith("/")) {
|
|
324167
|
+
const isSlashArgument = textBeforeCursor.startsWith("/") && textBeforeCursor.includes(" ");
|
|
324168
|
+
const isAtMention = extractAtPrefix(textBeforeCursor) !== null;
|
|
324169
|
+
if (isSlashArgument || isAtMention) trigger();
|
|
324170
|
+
return;
|
|
324171
|
+
}
|
|
324172
|
+
if (textBeforeCursor.endsWith(" ") && textBeforeCursor.startsWith("/") && textBeforeCursor.includes(" ")) trigger();
|
|
323501
324173
|
}
|
|
323502
324174
|
};
|
|
323503
324175
|
/**
|
|
@@ -323661,6 +324333,7 @@ function createTUIState(options) {
|
|
|
323661
324333
|
const theme = currentTheme;
|
|
323662
324334
|
const terminal = new ProcessTerminal();
|
|
323663
324335
|
const ui = new TUI(terminal);
|
|
324336
|
+
ui.setClearOnShrink(true);
|
|
323664
324337
|
return {
|
|
323665
324338
|
ui,
|
|
323666
324339
|
terminal,
|
|
@@ -323891,28 +324564,6 @@ function isOAuthLoginRequiredError(error) {
|
|
|
323891
324564
|
return error.code === OAUTH_LOGIN_REQUIRED_CODE;
|
|
323892
324565
|
}
|
|
323893
324566
|
//#endregion
|
|
323894
|
-
//#region src/tui/utils/terminal-focus.ts
|
|
323895
|
-
function installTerminalFocusTracking(state) {
|
|
323896
|
-
state.terminalState.focused = true;
|
|
323897
|
-
const disposeInputListener = state.ui.addInputListener((data) => handleTerminalFocusInput(state.terminalState, data));
|
|
323898
|
-
state.terminal.write(ENABLE_TERMINAL_FOCUS_REPORTING);
|
|
323899
|
-
return () => {
|
|
323900
|
-
disposeInputListener();
|
|
323901
|
-
state.terminal.write(DISABLE_TERMINAL_FOCUS_REPORTING);
|
|
323902
|
-
state.terminalState.focused = true;
|
|
323903
|
-
};
|
|
323904
|
-
}
|
|
323905
|
-
function handleTerminalFocusInput(state, data) {
|
|
323906
|
-
if (data === TERMINAL_FOCUS_IN) {
|
|
323907
|
-
state.focused = true;
|
|
323908
|
-
return { consume: true };
|
|
323909
|
-
}
|
|
323910
|
-
if (data === TERMINAL_FOCUS_OUT) {
|
|
323911
|
-
state.focused = false;
|
|
323912
|
-
return { consume: true };
|
|
323913
|
-
}
|
|
323914
|
-
}
|
|
323915
|
-
//#endregion
|
|
323916
324567
|
//#region src/tui/utils/terminal-theme.ts
|
|
323917
324568
|
function createTerminalThemeInputState() {
|
|
323918
324569
|
return { osc11Buffer: "" };
|
|
@@ -324035,6 +324686,10 @@ function readTmuxOptionFromProcess(option) {
|
|
|
324035
324686
|
}
|
|
324036
324687
|
//#endregion
|
|
324037
324688
|
//#region src/tui/kimi-tui.ts
|
|
324689
|
+
function loadingTipKind(mode) {
|
|
324690
|
+
if (mode === "waiting" || mode === "tool") return "moon";
|
|
324691
|
+
if (mode === "composing") return "composing";
|
|
324692
|
+
}
|
|
324038
324693
|
function sameStringArrays(a, b) {
|
|
324039
324694
|
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
324040
324695
|
}
|
|
@@ -324090,6 +324745,7 @@ var KimiTUI = class {
|
|
|
324090
324745
|
aborted = false;
|
|
324091
324746
|
terminalFocusTrackingDispose;
|
|
324092
324747
|
terminalThemeTrackingDispose;
|
|
324748
|
+
clipboardImageHintController;
|
|
324093
324749
|
uninstallRainbowDance;
|
|
324094
324750
|
signalCleanupHandlers = [];
|
|
324095
324751
|
isShuttingDown = false;
|
|
@@ -324097,6 +324753,7 @@ var KimiTUI = class {
|
|
|
324097
324753
|
migrateOnly;
|
|
324098
324754
|
startupNotice;
|
|
324099
324755
|
lastActivityMode;
|
|
324756
|
+
currentLoadingTip = void 0;
|
|
324100
324757
|
lastHistoryContent;
|
|
324101
324758
|
streamingUI;
|
|
324102
324759
|
authFlow;
|
|
@@ -324292,9 +324949,21 @@ var KimiTUI = class {
|
|
|
324292
324949
|
}
|
|
324293
324950
|
startEventLoop() {
|
|
324294
324951
|
this.state.ui.start();
|
|
324952
|
+
this.startClipboardImageHintController();
|
|
324295
324953
|
this.terminalFocusTrackingDispose = installTerminalFocusTracking(this.state);
|
|
324296
324954
|
this.refreshTerminalThemeTracking();
|
|
324297
324955
|
}
|
|
324956
|
+
startClipboardImageHintController() {
|
|
324957
|
+
this.clipboardImageHintController = new ClipboardImageHintController({
|
|
324958
|
+
ui: this.state.ui,
|
|
324959
|
+
footer: this.state.footer,
|
|
324960
|
+
getModelSupportsImage: () => this.supportsCurrentModelCapability("image_in"),
|
|
324961
|
+
requestRender: () => {
|
|
324962
|
+
this.state.ui.requestRender();
|
|
324963
|
+
}
|
|
324964
|
+
});
|
|
324965
|
+
this.clipboardImageHintController.start();
|
|
324966
|
+
}
|
|
324298
324967
|
startBackgroundFdAutocomplete() {
|
|
324299
324968
|
if (this.fdPath !== null || this.fdDownloadStarted) return;
|
|
324300
324969
|
this.fdDownloadStarted = true;
|
|
@@ -324471,6 +325140,8 @@ var KimiTUI = class {
|
|
|
324471
325140
|
}
|
|
324472
325141
|
disposeTerminalTracking() {
|
|
324473
325142
|
this.stopTerminalThemeTracking();
|
|
325143
|
+
this.clipboardImageHintController?.stop();
|
|
325144
|
+
this.clipboardImageHintController = void 0;
|
|
324474
325145
|
this.terminalFocusTrackingDispose?.();
|
|
324475
325146
|
this.terminalFocusTrackingDispose = void 0;
|
|
324476
325147
|
}
|
|
@@ -325116,6 +325787,15 @@ var KimiTUI = class {
|
|
|
325116
325787
|
}
|
|
325117
325788
|
updateActivityPane() {
|
|
325118
325789
|
const effectiveMode = this.resolveActivityPaneMode();
|
|
325790
|
+
const tipKind = loadingTipKind(effectiveMode);
|
|
325791
|
+
if (effectiveMode === "idle" || effectiveMode === "session" || effectiveMode === "hidden") this.currentLoadingTip = void 0;
|
|
325792
|
+
else if (tipKind !== void 0 && (this.currentLoadingTip === void 0 || this.currentLoadingTip.kind !== tipKind)) {
|
|
325793
|
+
const previousTip = this.currentLoadingTip?.tip;
|
|
325794
|
+
this.currentLoadingTip = {
|
|
325795
|
+
kind: tipKind,
|
|
325796
|
+
tip: pickRandomWorkingTip(previousTip)?.text
|
|
325797
|
+
};
|
|
325798
|
+
}
|
|
325119
325799
|
this.syncTerminalProgress(this.shouldShowTerminalProgress(effectiveMode));
|
|
325120
325800
|
const placeSpinnerInAgentSwarm = this.shouldPlaceActivitySpinnerInAgentSwarm(effectiveMode);
|
|
325121
325801
|
const activityModeKey = `${effectiveMode}:${placeSpinnerInAgentSwarm ? "swarm" : "pane"}`;
|
|
@@ -325137,7 +325817,8 @@ var KimiTUI = class {
|
|
|
325137
325817
|
if (placeSpinnerInAgentSwarm) break;
|
|
325138
325818
|
this.state.activityContainer.addChild(new ActivityPaneComponent({
|
|
325139
325819
|
mode: "waiting",
|
|
325140
|
-
spinner
|
|
325820
|
+
spinner,
|
|
325821
|
+
tip: this.currentLoadingTip?.tip
|
|
325141
325822
|
}));
|
|
325142
325823
|
break;
|
|
325143
325824
|
}
|
|
@@ -325150,7 +325831,8 @@ var KimiTUI = class {
|
|
|
325150
325831
|
this.syncAgentSwarmActivitySpinner(void 0);
|
|
325151
325832
|
this.state.activityContainer.addChild(new ActivityPaneComponent({
|
|
325152
325833
|
mode: "composing",
|
|
325153
|
-
spinner
|
|
325834
|
+
spinner,
|
|
325835
|
+
tip: this.currentLoadingTip?.tip
|
|
325154
325836
|
}));
|
|
325155
325837
|
break;
|
|
325156
325838
|
}
|
|
@@ -325160,7 +325842,8 @@ var KimiTUI = class {
|
|
|
325160
325842
|
if (placeSpinnerInAgentSwarm) break;
|
|
325161
325843
|
this.state.activityContainer.addChild(new ActivityPaneComponent({
|
|
325162
325844
|
mode: "tool",
|
|
325163
|
-
spinner
|
|
325845
|
+
spinner,
|
|
325846
|
+
tip: this.currentLoadingTip?.tip
|
|
325164
325847
|
}));
|
|
325165
325848
|
break;
|
|
325166
325849
|
}
|
|
@@ -325199,6 +325882,10 @@ var KimiTUI = class {
|
|
|
325199
325882
|
for (const child of this.state.transcriptContainer.children) if (isExpandable(child)) child.setExpanded(this.state.toolOutputExpanded);
|
|
325200
325883
|
this.state.ui.requestRender();
|
|
325201
325884
|
}
|
|
325885
|
+
toggleTodoPanelExpansion() {
|
|
325886
|
+
this.state.todoPanel.toggleExpanded();
|
|
325887
|
+
this.state.ui.requestRender();
|
|
325888
|
+
}
|
|
325202
325889
|
async detachCurrentForegroundTask() {
|
|
325203
325890
|
const session = this.session;
|
|
325204
325891
|
if (session === void 0) {
|