neoctl 0.1.6 → 0.1.7
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/README.md +18 -9
- package/dist/agents/local-agent-task.js +2 -1
- package/dist/agents/local-agent-task.js.map +1 -1
- package/dist/core/query.js +34 -1
- package/dist/core/query.js.map +1 -1
- package/dist/core/smoke-core-loop.js +34 -3
- package/dist/core/smoke-core-loop.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/config.d.ts +5 -2
- package/dist/model/config.js +21 -1
- package/dist/model/config.js.map +1 -1
- package/dist/model/env.js +10 -6
- package/dist/model/env.js.map +1 -1
- package/dist/model/kimi-adapter.d.ts +29 -0
- package/dist/model/kimi-adapter.js +108 -0
- package/dist/model/kimi-adapter.js.map +1 -0
- package/dist/model/model-metadata.json +51 -2
- package/dist/model/openai-chat-mapper.d.ts +1 -0
- package/dist/model/openai-chat-mapper.js +7 -3
- package/dist/model/openai-chat-mapper.js.map +1 -1
- package/dist/model/provider-factory.js +16 -0
- package/dist/model/provider-factory.js.map +1 -1
- package/dist/open-directory.d.ts +1 -0
- package/dist/open-directory.js +26 -0
- package/dist/open-directory.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.js +12 -0
- package/dist/paths.js.map +1 -0
- package/dist/repl/commands.d.ts +2 -0
- package/dist/repl/commands.js +3 -0
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.js +168 -30
- package/dist/repl/index.js.map +1 -1
- package/dist/session/session-store.js +2 -2
- package/dist/session/session-store.js.map +1 -1
- package/dist/tips.d.ts +10 -0
- package/dist/tips.js +168 -0
- package/dist/tips.js.map +1 -0
- package/dist/web/html.d.ts +1 -0
- package/dist/web/html.js +697 -0
- package/dist/web/html.js.map +1 -0
- package/dist/web/index.d.ts +2 -0
- package/dist/web/index.js +1465 -0
- package/dist/web/index.js.map +1 -0
- package/package.json +4 -1
package/dist/repl/index.js
CHANGED
|
@@ -27,6 +27,8 @@ import { isModelReasoningArgument, isValidReplCommandLine, parseReplCommand, hel
|
|
|
27
27
|
import { estimateMarkdownLineCount, markdownRenderKey, MarkdownText } from "./markdown-renderer.js";
|
|
28
28
|
import { writeSessionMarkdownExport } from "../session/session-export.js";
|
|
29
29
|
import { readClipboard } from "./clipboard.js";
|
|
30
|
+
import { formatTipLine, initialTipIndex, tipAt } from "../tips.js";
|
|
31
|
+
import { openDirectory } from "../open-directory.js";
|
|
30
32
|
const e = React.createElement;
|
|
31
33
|
class SessionUsageTracker {
|
|
32
34
|
totals = emptyUsageTotals();
|
|
@@ -172,7 +174,7 @@ async function createRuntime() {
|
|
|
172
174
|
};
|
|
173
175
|
}
|
|
174
176
|
function formatCreatedEnvNotice(path) {
|
|
175
|
-
return `Created default config file: ${path}\nSet MODEL_PROVIDER and the matching provider section (for example OPENAI_API_KEY), then restart neo.`;
|
|
177
|
+
return `Created default config file: ${path}\nSet MODEL_PROVIDER and the matching provider section (for example OPENAI_API_KEY or KIMI_API_KEY), then restart neo.`;
|
|
176
178
|
}
|
|
177
179
|
function parseResumeFlag(value) {
|
|
178
180
|
if (!value)
|
|
@@ -361,6 +363,7 @@ function InkRepl({ runtime }) {
|
|
|
361
363
|
const queuedAttachmentsRef = useRef(undefined);
|
|
362
364
|
const [cursor, setCursor] = useState(0);
|
|
363
365
|
const [promptPlaceholder, setPromptPlaceholder] = useState(undefined);
|
|
366
|
+
const [tipIndex, setTipIndex] = useState(() => initialTipIndex(runtime.engine.snapshot().session?.sessionId ?? process.cwd()));
|
|
364
367
|
const [busy, setBusy] = useState(false);
|
|
365
368
|
const [status, setStatus] = useState(() => initialStatus(runtime));
|
|
366
369
|
const sessionTitleRef = useRef(sessionTerminalTitle(runtime.engine.snapshot().session));
|
|
@@ -480,9 +483,11 @@ function InkRepl({ runtime }) {
|
|
|
480
483
|
}, PASTE_STATUS_DISPLAY_MS);
|
|
481
484
|
pasteStatusTimerRef.current = timer;
|
|
482
485
|
};
|
|
486
|
+
const advanceTip = () => setTipIndex((current) => current + 1);
|
|
483
487
|
const insertAtCursor = (value) => {
|
|
484
488
|
const currentText = inputRef.current;
|
|
485
489
|
const currentCursor = cursorRef.current;
|
|
490
|
+
advanceTip();
|
|
486
491
|
setPromptState(`${currentText.slice(0, currentCursor)}${value}${currentText.slice(currentCursor)}`, currentCursor + value.length);
|
|
487
492
|
};
|
|
488
493
|
const insertAttachmentLabel = (attachment) => {
|
|
@@ -842,6 +847,18 @@ function InkRepl({ runtime }) {
|
|
|
842
847
|
}
|
|
843
848
|
return;
|
|
844
849
|
}
|
|
850
|
+
if (command.type === "env") {
|
|
851
|
+
const envDirectory = path.dirname(runtime.envPath);
|
|
852
|
+
try {
|
|
853
|
+
await fs.mkdir(envDirectory, { recursive: true });
|
|
854
|
+
await openDirectory(envDirectory);
|
|
855
|
+
append({ kind: "system", title: "System", text: `Opened env directory: ${envDirectory}`, format: "plain", previewStyle: "summary" });
|
|
856
|
+
}
|
|
857
|
+
catch (error) {
|
|
858
|
+
append({ kind: "error", text: `Failed to open env directory ${envDirectory}: ${error instanceof Error ? error.message : String(error)}`, format: "plain" });
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
845
862
|
if (command.type === "sessions") {
|
|
846
863
|
await handleSessionsCommand(runtime, setSessionsBrowser, (line) => append(line));
|
|
847
864
|
return;
|
|
@@ -939,6 +956,7 @@ function InkRepl({ runtime }) {
|
|
|
939
956
|
}
|
|
940
957
|
};
|
|
941
958
|
useEffect(() => {
|
|
959
|
+
setTipIndex(initialTipIndex(runtime.engine.snapshot().session?.sessionId ?? process.cwd()));
|
|
942
960
|
setLines(initialLines(runtime, lineId));
|
|
943
961
|
assistantLineId.current = undefined;
|
|
944
962
|
thinkingLineId.current = undefined;
|
|
@@ -955,9 +973,13 @@ function InkRepl({ runtime }) {
|
|
|
955
973
|
const width = terminalSize.columns;
|
|
956
974
|
const inputLockedByQueue = busy && queuedInput !== undefined;
|
|
957
975
|
const prompt = promptPrefix(busy);
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
const
|
|
976
|
+
const currentTip = tipAt(tipIndex);
|
|
977
|
+
const activePlaceholder = input.length === 0 ? promptPlaceholder ?? currentTip.placeholder : undefined;
|
|
978
|
+
const promptDisplayText = input;
|
|
979
|
+
const promptDisplayCursor = cursor;
|
|
980
|
+
const promptLayoutText = activePlaceholder ? ` ${activePlaceholder}` : promptDisplayText;
|
|
981
|
+
const promptLayoutCursor = activePlaceholder ? 0 : promptDisplayCursor;
|
|
982
|
+
const slashCompletions = inputLockedByQueue || (input.length === 0 && promptPlaceholder !== undefined) || loginForm ? [] : slashCommandCompletions(input, cursor);
|
|
961
983
|
const visibleSlashCompletionCount = slashCompletions.length;
|
|
962
984
|
const selectedSlashCompletionIndex = visibleSlashCompletionCount === 0
|
|
963
985
|
? 0
|
|
@@ -965,7 +987,7 @@ function InkRepl({ runtime }) {
|
|
|
965
987
|
if (selectedSlashCompletionIndex !== slashCompletionIndexRef.current) {
|
|
966
988
|
slashCompletionIndexRef.current = selectedSlashCompletionIndex;
|
|
967
989
|
}
|
|
968
|
-
const promptHeight = promptTextView(
|
|
990
|
+
const promptHeight = promptTextView(promptLayoutText, promptLayoutCursor, width, prompt).length + slashCompletionViewHeight(slashCompletions) + (queuedInput !== undefined ? QUEUED_INPUT_RENDER_ROWS : 0) + (pasteStatus ? 1 : 0);
|
|
969
991
|
const firstDynamicLineIndex = lines.findIndex((line) => lineNeedsDynamicRender(line, messageContentWidth(width)));
|
|
970
992
|
const staticLines = firstDynamicLineIndex === -1 ? lines : lines.slice(0, firstDynamicLineIndex);
|
|
971
993
|
const dynamicLines = firstDynamicLineIndex === -1 ? [] : lines.slice(firstDynamicLineIndex);
|
|
@@ -1088,6 +1110,10 @@ function InkRepl({ runtime }) {
|
|
|
1088
1110
|
if (key.backspace || key.delete) {
|
|
1089
1111
|
const currentText = inputRef.current;
|
|
1090
1112
|
const currentCursor = cursorRef.current;
|
|
1113
|
+
if (currentText.length === 0) {
|
|
1114
|
+
setTipIndex((current) => current + 1);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1091
1117
|
if (currentCursor > 0) {
|
|
1092
1118
|
setPromptState(`${currentText.slice(0, currentCursor - 1)}${currentText.slice(currentCursor)}`, currentCursor - 1);
|
|
1093
1119
|
}
|
|
@@ -1099,6 +1125,10 @@ function InkRepl({ runtime }) {
|
|
|
1099
1125
|
setSlashCompletionSelection((slashCompletionIndexRef.current + completionCount - SLASH_COMPLETION_PAGE_SIZE) % completionCount);
|
|
1100
1126
|
return;
|
|
1101
1127
|
}
|
|
1128
|
+
if (inputRef.current.length === 0) {
|
|
1129
|
+
setTipIndex((current) => current - 1);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1102
1132
|
setPromptState(inputRef.current, cursorRef.current - 1);
|
|
1103
1133
|
return;
|
|
1104
1134
|
}
|
|
@@ -1108,18 +1138,32 @@ function InkRepl({ runtime }) {
|
|
|
1108
1138
|
setSlashCompletionSelection((slashCompletionIndexRef.current + SLASH_COMPLETION_PAGE_SIZE) % completionCount);
|
|
1109
1139
|
return;
|
|
1110
1140
|
}
|
|
1141
|
+
if (inputRef.current.length === 0) {
|
|
1142
|
+
setTipIndex((current) => current + 1);
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1111
1145
|
setPromptState(inputRef.current, cursorRef.current + 1);
|
|
1112
1146
|
return;
|
|
1113
1147
|
}
|
|
1114
1148
|
if (key.home) {
|
|
1115
|
-
|
|
1149
|
+
if (inputRef.current.length === 0)
|
|
1150
|
+
setTipIndex(0);
|
|
1151
|
+
else
|
|
1152
|
+
setPromptState(inputRef.current, 0);
|
|
1116
1153
|
return;
|
|
1117
1154
|
}
|
|
1118
1155
|
if (key.end) {
|
|
1119
|
-
|
|
1156
|
+
if (inputRef.current.length === 0)
|
|
1157
|
+
setTipIndex((current) => current + 1);
|
|
1158
|
+
else
|
|
1159
|
+
setPromptState(inputRef.current, inputRef.current.length);
|
|
1120
1160
|
return;
|
|
1121
1161
|
}
|
|
1122
1162
|
if (key.upArrow) {
|
|
1163
|
+
if (inputRef.current.length === 0 && history.current.length === 0) {
|
|
1164
|
+
setTipIndex((current) => current - 1);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1123
1167
|
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current);
|
|
1124
1168
|
if (completionCount > 0) {
|
|
1125
1169
|
setSlashCompletionSelection((slashCompletionIndexRef.current + completionCount - 1) % completionCount);
|
|
@@ -1133,6 +1177,10 @@ function InkRepl({ runtime }) {
|
|
|
1133
1177
|
return;
|
|
1134
1178
|
}
|
|
1135
1179
|
if (key.downArrow) {
|
|
1180
|
+
if (inputRef.current.length === 0 && historyIndexRef.current === undefined) {
|
|
1181
|
+
setTipIndex((current) => current + 1);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1136
1184
|
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current);
|
|
1137
1185
|
if (completionCount > 0) {
|
|
1138
1186
|
setSlashCompletionSelection((slashCompletionIndexRef.current + 1) % completionCount);
|
|
@@ -1154,6 +1202,10 @@ function InkRepl({ runtime }) {
|
|
|
1154
1202
|
}
|
|
1155
1203
|
if (key.tab) {
|
|
1156
1204
|
const currentText = inputRef.current;
|
|
1205
|
+
if (currentText.length === 0) {
|
|
1206
|
+
setTipIndex((current) => current + 1);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1157
1209
|
const currentCursor = cursorRef.current;
|
|
1158
1210
|
const completions = slashCommandCompletions(currentText, currentCursor);
|
|
1159
1211
|
const completion = completions[Math.min(slashCompletionIndexRef.current, completions.length - 1)];
|
|
@@ -1165,9 +1217,10 @@ function InkRepl({ runtime }) {
|
|
|
1165
1217
|
}
|
|
1166
1218
|
if (value && !key.ctrl && !key.meta) {
|
|
1167
1219
|
insertAtCursor(value);
|
|
1220
|
+
return;
|
|
1168
1221
|
}
|
|
1169
1222
|
});
|
|
1170
|
-
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: dynamicLines, width, liveMaxLines: liveViewportLines, lineIndexOffset: staticLines.length, onMarkdownRenderComplete: markLineRendered }), sessionsBrowser ? e(SessionsBrowser, { state: sessionsBrowser, width }) : null, loginForm ? e(LoginFormView, { state: loginForm, width }) : null, e(StatusBar, { status, animationTick, width }), backgroundTaskCount > 0 ? e(BackgroundTaskStatusLine, { count: backgroundTaskCount, width }) : null, pasteStatus ? e(PasteStatusLine, { text: pasteStatus, width }) : null, queuedInput !== undefined ? e(QueuedInputLine, { text: queuedInput, width }) : null, e(PromptLine, { text: promptDisplayText, cursor: promptDisplayCursor, busy, locked: inputLockedByQueue, placeholder: input.length === 0 && promptPlaceholder !== undefined, width, prompt, slashCompletions, selectedSlashCompletionIndex, attachments }));
|
|
1223
|
+
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: dynamicLines, width, liveMaxLines: liveViewportLines, lineIndexOffset: staticLines.length, onMarkdownRenderComplete: markLineRendered }), sessionsBrowser ? e(SessionsBrowser, { state: sessionsBrowser, width }) : null, loginForm ? e(LoginFormView, { state: loginForm, width }) : null, e(StatusBar, { status, animationTick, width }), backgroundTaskCount > 0 ? e(BackgroundTaskStatusLine, { count: backgroundTaskCount, width }) : null, pasteStatus ? e(PasteStatusLine, { text: pasteStatus, width }) : null, queuedInput !== undefined ? e(QueuedInputLine, { text: queuedInput, width }) : null, e(PromptLine, { text: promptDisplayText, cursor: promptDisplayCursor, busy, locked: inputLockedByQueue, placeholder: input.length === 0 && promptPlaceholder !== undefined, ghostText: activePlaceholder, width, prompt, slashCompletions, selectedSlashCompletionIndex, attachments }));
|
|
1171
1224
|
}
|
|
1172
1225
|
const MessageList = React.memo(function MessageList({ lines, width, liveMaxLines, lineIndexOffset = 0, onMarkdownRenderComplete }) {
|
|
1173
1226
|
const contentWidth = messageContentWidth(width);
|
|
@@ -1193,9 +1246,17 @@ function MessageLine({ line, width, contentWidth = messageContentWidth(width), t
|
|
|
1193
1246
|
const display = displayWindowForLine(line, summaryWidth, line.live ? liveMaxLines : undefined);
|
|
1194
1247
|
return e(Box, { flexDirection: "row" }, useRoleMarker ? e(Text, { color: markerColorForKind(line.kind) }, messageRoleMarker(line.kind)) : null, e(Box, { flexDirection: "column", width: summaryWidth }, ...renderDisplayText(line, summaryWidth, display.maxLines, display.skipTop)));
|
|
1195
1248
|
}
|
|
1196
|
-
const
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1249
|
+
const useRoleMarker = !titleProvidesToolMarker(line);
|
|
1250
|
+
const lineWidth = useRoleMarker ? contentWidth : toolWidth;
|
|
1251
|
+
const clipPendingMarkdown = !line.live && onMarkdownRenderComplete !== undefined && lineNeedsDynamicRender(line, lineWidth);
|
|
1252
|
+
const display = displayWindowForLine(line, lineWidth, line.live || clipPendingMarkdown ? liveMaxLines : undefined);
|
|
1253
|
+
const contentNodes = [];
|
|
1254
|
+
if (line.title)
|
|
1255
|
+
contentNodes.push(renderBlockTitle(line));
|
|
1256
|
+
if (line.bodyTitle)
|
|
1257
|
+
contentNodes.push(e(Text, { key: `body-title-${line.id}`, bold: true }, line.bodyTitle));
|
|
1258
|
+
contentNodes.push(...renderDisplayText(line, lineWidth, display.maxLines, display.skipTop, onMarkdownRenderComplete));
|
|
1259
|
+
return e(Box, { flexDirection: "row" }, useRoleMarker ? e(Text, { color: markerColorForKind(line.kind) }, messageRoleMarker(line.kind)) : null, e(Box, { flexDirection: "column", width: lineWidth }, ...contentNodes));
|
|
1199
1260
|
}
|
|
1200
1261
|
function displayWindowForLine(line, width, maxLines) {
|
|
1201
1262
|
if (maxLines === undefined)
|
|
@@ -1265,12 +1326,21 @@ function summaryTitle(line) {
|
|
|
1265
1326
|
function summaryUsesRoleMarker(line) {
|
|
1266
1327
|
return line.previewStyle === "summary" && (line.kind === "system" || line.kind === "meta");
|
|
1267
1328
|
}
|
|
1329
|
+
function titleProvidesToolMarker(line) {
|
|
1330
|
+
return line.kind === "tool" && !!line.title && (line.title.startsWith("◇ ") || line.title.startsWith("◆ "));
|
|
1331
|
+
}
|
|
1268
1332
|
function titleStatusMarker(status) {
|
|
1269
1333
|
return status === "success" ? "✓" : "✗";
|
|
1270
1334
|
}
|
|
1271
1335
|
function titleStatusColor(status) {
|
|
1272
1336
|
return status === "success" ? "green" : "red";
|
|
1273
1337
|
}
|
|
1338
|
+
function renderBlockTitle(line) {
|
|
1339
|
+
const title = line.title ?? titleForKind(line.kind);
|
|
1340
|
+
if (!line.titleStatus)
|
|
1341
|
+
return e(Text, { key: `title-${line.id}`, color: colorForKind(line.kind), bold: true }, title);
|
|
1342
|
+
return e(Text, { key: `title-${line.id}`, color: colorForKind(line.kind), bold: true }, `${title} `, e(Text, { color: titleStatusColor(line.titleStatus), bold: true }, titleStatusMarker(line.titleStatus)));
|
|
1343
|
+
}
|
|
1274
1344
|
function renderSummaryBlock(line, width, maxLines, skipTop = 0) {
|
|
1275
1345
|
const allPreviewLines = renderSummaryLines(line, width);
|
|
1276
1346
|
const preview = clipStrings(allPreviewLines, maxLines, skipTop);
|
|
@@ -1516,7 +1586,7 @@ function renderCompactStatusSegments(status, animationTick, width, inputTokens,
|
|
|
1516
1586
|
const context = renderContextParts(status.metrics);
|
|
1517
1587
|
const fixedText = [
|
|
1518
1588
|
phaseText,
|
|
1519
|
-
`ctx ${context.
|
|
1589
|
+
`ctx ${context.percent} of ${context.limit}`,
|
|
1520
1590
|
`↑ ${inputValue}`,
|
|
1521
1591
|
`↓ ${outputValue}`,
|
|
1522
1592
|
].join(STATUS_SEPARATOR);
|
|
@@ -1534,8 +1604,8 @@ function renderCompactStatusSegments(status, animationTick, width, inputTokens,
|
|
|
1534
1604
|
{ text: model },
|
|
1535
1605
|
statusDividerSegment(),
|
|
1536
1606
|
statusLabelSegment("ctx"),
|
|
1537
|
-
{ text: ` ${context.
|
|
1538
|
-
{ text: `
|
|
1607
|
+
{ text: ` ${context.percent}`, color: contextColor(status.metrics) },
|
|
1608
|
+
{ text: ` of ${context.limit}` },
|
|
1539
1609
|
statusDividerSegment(),
|
|
1540
1610
|
statusLabelSegment("↑", tokenInputColor),
|
|
1541
1611
|
{ text: ` ${inputValue}` },
|
|
@@ -1681,10 +1751,16 @@ function selectedSlashCommandCompletion(text, cursor, selectedIndex) {
|
|
|
1681
1751
|
return undefined;
|
|
1682
1752
|
return completions[Math.max(0, Math.min(selectedIndex, completions.length - 1))];
|
|
1683
1753
|
}
|
|
1684
|
-
function PromptLine({ text, cursor, busy, locked, placeholder = false, width, prompt, slashCompletions, selectedSlashCompletionIndex, attachments }) {
|
|
1685
|
-
const
|
|
1754
|
+
function PromptLine({ text, cursor, busy, locked, placeholder = false, ghostText, width, prompt, slashCompletions, selectedSlashCompletionIndex, attachments }) {
|
|
1755
|
+
const displayText = text.length === 0 && ghostText ? ` ${ghostText}` : text;
|
|
1756
|
+
const displayCursor = text.length === 0 && ghostText ? 0 : cursor;
|
|
1757
|
+
const visualLines = promptTextView(displayText, displayCursor, width, prompt);
|
|
1686
1758
|
const inputColor = placeholder ? "gray" : (!locked && isValidReplCommandLine(text) ? "cyan" : undefined);
|
|
1687
|
-
return e(Box, { flexDirection: "column" }, ...visualLines.map((line, index) =>
|
|
1759
|
+
return e(Box, { flexDirection: "column" }, ...visualLines.map((line, index) => {
|
|
1760
|
+
const isGhostLine = text.length === 0 && ghostText !== undefined;
|
|
1761
|
+
const afterColor = isGhostLine ? "gray" : inputColor;
|
|
1762
|
+
return e(Box, { key: `prompt-${index}`, height: 1, overflow: "hidden" }, e(Text, { color: locked ? "gray" : "cyan" }, index === 0 ? prompt : " ".repeat(prompt.length)), ...renderPromptPart(line.before, inputColor, attachments, `prompt-${index}-before`), e(Text, { key: `prompt-${index}-cursor`, inverse: true, color: inputColor }, line.selected), ...renderPromptPart(line.after, afterColor, attachments, `prompt-${index}-after`));
|
|
1763
|
+
}), ...SlashCompletionLines({ completions: slashCompletions, width, prompt, selectedIndex: selectedSlashCompletionIndex }));
|
|
1688
1764
|
}
|
|
1689
1765
|
function PasteStatusLine({ text, width: terminalWidth }) {
|
|
1690
1766
|
const width = statusBarWidth(terminalWidth);
|
|
@@ -1821,7 +1897,11 @@ function currentModelProvider() {
|
|
|
1821
1897
|
return parseLoginProvider(process.env.MODEL_PROVIDER) ?? "openai";
|
|
1822
1898
|
}
|
|
1823
1899
|
function modelEnvKeyForProvider(provider) {
|
|
1824
|
-
|
|
1900
|
+
if (provider === "deepseek")
|
|
1901
|
+
return "DEEPSEEK_MODEL";
|
|
1902
|
+
if (provider === "kimi")
|
|
1903
|
+
return "KIMI_MODEL";
|
|
1904
|
+
return "OPENAI_MODEL";
|
|
1825
1905
|
}
|
|
1826
1906
|
function envValueForReasoning(reasoning) {
|
|
1827
1907
|
if (reasoning === null)
|
|
@@ -2132,11 +2212,11 @@ function initialLines(runtime, lineId) {
|
|
|
2132
2212
|
? ` Session: ${session.sessionId}${session.resumedMessages > 0 ? ` (${session.resumedMessages} resumed messages)` : ""}.`
|
|
2133
2213
|
: "";
|
|
2134
2214
|
const lines = [
|
|
2135
|
-
{ id: 0, kind: "system", title: "System", text: `Interactive UI enabled. Type /help for commands.${suffix}`, previewStyle: "summary" },
|
|
2215
|
+
{ id: 0, kind: "system", title: "System", text: `Interactive UI enabled. Type /help for commands.${suffix}\n${formatTipLine(tipAt(initialTipIndex(session?.sessionId ?? process.cwd())))}`, previewStyle: "summary" },
|
|
2136
2216
|
];
|
|
2137
2217
|
lineId.current = 0;
|
|
2138
2218
|
if (runtime.envNotice)
|
|
2139
|
-
lines.push({ id: ++lineId.current, kind: "system", title: "Config", text: runtime.envNotice, previewStyle: "summary" });
|
|
2219
|
+
lines.push({ id: ++lineId.current, kind: "system", title: "Config", text: runtime.envNotice, format: "plain", previewStyle: "summary" });
|
|
2140
2220
|
for (const line of restoredHistoryLines(runtime))
|
|
2141
2221
|
lines.push({ id: ++lineId.current, ...line });
|
|
2142
2222
|
return lines;
|
|
@@ -2155,7 +2235,7 @@ function restoredHistoryLines(runtime) {
|
|
|
2155
2235
|
}
|
|
2156
2236
|
return lines;
|
|
2157
2237
|
}
|
|
2158
|
-
const LOGIN_PROVIDERS = ["openai", "deepseek"];
|
|
2238
|
+
const LOGIN_PROVIDERS = ["openai", "deepseek", "kimi"];
|
|
2159
2239
|
const SHARED_LOGIN_FIELDS = [
|
|
2160
2240
|
{ key: "reasoningEffort", label: "Reasoning effort", envKey: "MODEL_REASONING_EFFORT", scope: "shared", options: ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"] },
|
|
2161
2241
|
{ key: "reasoningSummary", label: "Reasoning summary", envKey: "MODEL_REASONING_SUMMARY", scope: "shared", options: ["", "auto", "concise", "detailed"] },
|
|
@@ -2180,6 +2260,13 @@ const LOGIN_FIELD_DEFINITIONS = {
|
|
|
2180
2260
|
{ key: "fallbackModel", label: "Fallback model", envKey: "DEEPSEEK_FALLBACK_MODEL", scope: "provider" },
|
|
2181
2261
|
...SHARED_LOGIN_FIELDS,
|
|
2182
2262
|
],
|
|
2263
|
+
kimi: [
|
|
2264
|
+
{ key: "apiKey", label: "API key", envKey: "KIMI_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
|
|
2265
|
+
{ key: "baseUrl", label: "Base URL", envKey: "KIMI_BASE_URL", scope: "provider", placeholder: "https://api.moonshot.cn/v1" },
|
|
2266
|
+
{ key: "model", label: "Model", envKey: "KIMI_MODEL", scope: "provider", required: true, placeholder: "kimi-k2.6" },
|
|
2267
|
+
{ key: "fallbackModel", label: "Fallback model", envKey: "KIMI_FALLBACK_MODEL", scope: "provider" },
|
|
2268
|
+
...SHARED_LOGIN_FIELDS,
|
|
2269
|
+
],
|
|
2183
2270
|
};
|
|
2184
2271
|
const DEPRECATED_MODEL_ENV_KEYS = [
|
|
2185
2272
|
"MODEL_API_KEY",
|
|
@@ -2200,6 +2287,18 @@ const DEPRECATED_MODEL_ENV_KEYS = [
|
|
|
2200
2287
|
"DEEPSEEK_TIMEOUT_MS",
|
|
2201
2288
|
"DEEPSEEK_STREAM_IDLE_TIMEOUT_MS",
|
|
2202
2289
|
"DEEPSEEK_MAX_RETRIES",
|
|
2290
|
+
"KIMI_REASONING_EFFORT",
|
|
2291
|
+
"KIMI_REASONING_SUMMARY",
|
|
2292
|
+
"KIMI_MAX_OUTPUT_TOKENS",
|
|
2293
|
+
"KIMI_TIMEOUT_MS",
|
|
2294
|
+
"KIMI_STREAM_IDLE_TIMEOUT_MS",
|
|
2295
|
+
"KIMI_MAX_RETRIES",
|
|
2296
|
+
"MOONSHOT_REASONING_EFFORT",
|
|
2297
|
+
"MOONSHOT_REASONING_SUMMARY",
|
|
2298
|
+
"MOONSHOT_MAX_OUTPUT_TOKENS",
|
|
2299
|
+
"MOONSHOT_TIMEOUT_MS",
|
|
2300
|
+
"MOONSHOT_STREAM_IDLE_TIMEOUT_MS",
|
|
2301
|
+
"MOONSHOT_MAX_RETRIES",
|
|
2203
2302
|
];
|
|
2204
2303
|
function sessionsPageCount(state) {
|
|
2205
2304
|
return Math.max(1, Math.ceil(state.sessions.length / state.pageSize));
|
|
@@ -2394,7 +2493,7 @@ function validateLoginForm(state) {
|
|
|
2394
2493
|
}
|
|
2395
2494
|
function createLoginFormState(envPath = getUserDotEnvPath()) {
|
|
2396
2495
|
const env = parseEnvFileSafe(envPath);
|
|
2397
|
-
const currentProvider = parseLoginProvider(env.MODEL_PROVIDER ?? process.env.MODEL_PROVIDER) ?? (
|
|
2496
|
+
const currentProvider = parseLoginProvider(env.MODEL_PROVIDER ?? process.env.MODEL_PROVIDER) ?? guessLoginProvider(env);
|
|
2398
2497
|
return loginFormForProvider(currentProvider, envPath, env);
|
|
2399
2498
|
}
|
|
2400
2499
|
function loginFormForProvider(provider, envPath, env = parseEnvFileSafe(envPath)) {
|
|
@@ -2415,19 +2514,46 @@ function loginValuesForProvider(provider, env) {
|
|
|
2415
2514
|
for (const field of LOGIN_FIELD_DEFINITIONS[provider]) {
|
|
2416
2515
|
values[field.key] = env[field.envKey] ?? "";
|
|
2417
2516
|
}
|
|
2517
|
+
if (provider === "kimi") {
|
|
2518
|
+
values.apiKey ||= env.MOONSHOT_API_KEY ?? process.env.MOONSHOT_API_KEY ?? "";
|
|
2519
|
+
values.baseUrl ||= env.MOONSHOT_BASE_URL ?? process.env.MOONSHOT_BASE_URL ?? "";
|
|
2520
|
+
values.model ||= env.MOONSHOT_MODEL ?? process.env.MOONSHOT_MODEL ?? "";
|
|
2521
|
+
values.fallbackModel ||= env.MOONSHOT_FALLBACK_MODEL ?? process.env.MOONSHOT_FALLBACK_MODEL ?? "";
|
|
2522
|
+
}
|
|
2418
2523
|
if (!values.baseUrl)
|
|
2419
|
-
values.baseUrl = provider
|
|
2524
|
+
values.baseUrl = defaultBaseUrlForLoginProvider(provider);
|
|
2420
2525
|
if (!values.model)
|
|
2421
|
-
values.model = provider
|
|
2526
|
+
values.model = defaultModelForLoginProvider(provider);
|
|
2422
2527
|
if (provider === "openai" && !values.endpoint)
|
|
2423
2528
|
values.endpoint = "auto";
|
|
2424
2529
|
return values;
|
|
2425
2530
|
}
|
|
2426
2531
|
function parseLoginProvider(value) {
|
|
2427
|
-
if (value === "openai" || value === "deepseek")
|
|
2532
|
+
if (value === "openai" || value === "deepseek" || value === "kimi")
|
|
2428
2533
|
return value;
|
|
2429
2534
|
return undefined;
|
|
2430
2535
|
}
|
|
2536
|
+
function guessLoginProvider(env) {
|
|
2537
|
+
if (env.KIMI_API_KEY ?? env.MOONSHOT_API_KEY ?? process.env.KIMI_API_KEY ?? process.env.MOONSHOT_API_KEY)
|
|
2538
|
+
return "kimi";
|
|
2539
|
+
if (env.DEEPSEEK_API_KEY ?? process.env.DEEPSEEK_API_KEY)
|
|
2540
|
+
return "deepseek";
|
|
2541
|
+
return "openai";
|
|
2542
|
+
}
|
|
2543
|
+
function defaultBaseUrlForLoginProvider(provider) {
|
|
2544
|
+
if (provider === "deepseek")
|
|
2545
|
+
return "https://api.deepseek.com";
|
|
2546
|
+
if (provider === "kimi")
|
|
2547
|
+
return "https://api.moonshot.cn/v1";
|
|
2548
|
+
return "https://api.openai.com";
|
|
2549
|
+
}
|
|
2550
|
+
function defaultModelForLoginProvider(provider) {
|
|
2551
|
+
if (provider === "deepseek")
|
|
2552
|
+
return "deepseek-chat";
|
|
2553
|
+
if (provider === "kimi")
|
|
2554
|
+
return "kimi-k2.6";
|
|
2555
|
+
return "gpt-5.5";
|
|
2556
|
+
}
|
|
2431
2557
|
function loginFormViewHeight(state) {
|
|
2432
2558
|
return state.step === "provider" ? state.providers.length + 3 : LOGIN_FIELD_DEFINITIONS[state.provider].length + 4;
|
|
2433
2559
|
}
|
|
@@ -2444,7 +2570,7 @@ function LoginFormView({ state, width }) {
|
|
|
2444
2570
|
const visibleValue = formatLoginFieldValue(field, rawValue, selected ? state.cursor : undefined);
|
|
2445
2571
|
const placeholder = rawValue ? "" : (field.placeholder ? ` (${field.placeholder})` : "");
|
|
2446
2572
|
return e(Text, { key: field.key, color: "white" }, e(Text, { color: selected ? "black" : "white", backgroundColor: selected ? "cyan" : undefined }, `${index + 1}.`.padStart(3)), e(Text, { color: field.required ? "yellow" : "gray" }, ` ${field.label.padEnd(maxLabel)} `), e(Text, { color: field.scope === "shared" ? "blue" : "gray" }, field.scope === "shared" ? "shared " : "provider "), e(Text, { color: rawValue ? "white" : "gray" }, fitToWidth(`${visibleValue}${placeholder}`, Math.max(8, contentWidth - maxLabel - 14))));
|
|
2447
|
-
}), e(Text, { color: "gray" }, fitToWidth("↑/↓ field · ←/→ cursor · type edit · Tab cycle choices · Enter save · Esc back/cancel", contentWidth)), e(Text, { color: "gray" }, fitToWidth("Provider fields save as OPENAI_* / DEEPSEEK_*; shared runtime fields save as MODEL_*.", contentWidth)));
|
|
2573
|
+
}), e(Text, { color: "gray" }, fitToWidth("↑/↓ field · ←/→ cursor · type edit · Tab cycle choices · Enter save · Esc back/cancel", contentWidth)), e(Text, { color: "gray" }, fitToWidth("Provider fields save as OPENAI_* / DEEPSEEK_* / KIMI_*; shared runtime fields save as MODEL_*.", contentWidth)));
|
|
2448
2574
|
}
|
|
2449
2575
|
function formatLoginFieldValue(field, value, cursor) {
|
|
2450
2576
|
const display = field.secret && value ? "•".repeat(Math.min(value.length, 24)) : value;
|
|
@@ -2470,6 +2596,12 @@ function envEntriesForLoginForm(state) {
|
|
|
2470
2596
|
const value = (state.values[field.key] ?? "").trim();
|
|
2471
2597
|
entries[field.envKey] = value || undefined;
|
|
2472
2598
|
}
|
|
2599
|
+
if (state.provider === "kimi") {
|
|
2600
|
+
entries.MOONSHOT_API_KEY = undefined;
|
|
2601
|
+
entries.MOONSHOT_BASE_URL = undefined;
|
|
2602
|
+
entries.MOONSHOT_MODEL = undefined;
|
|
2603
|
+
entries.MOONSHOT_FALLBACK_MODEL = undefined;
|
|
2604
|
+
}
|
|
2473
2605
|
return entries;
|
|
2474
2606
|
}
|
|
2475
2607
|
function updateEnvContent(content, updates, removeKeys = []) {
|
|
@@ -2497,6 +2629,7 @@ function updateEnvContent(content, updates, removeKeys = []) {
|
|
|
2497
2629
|
appendEnvGroup(updatedLines, "# Neo active provider", grouped.active);
|
|
2498
2630
|
appendEnvGroup(updatedLines, "# OpenAI provider settings", grouped.openai);
|
|
2499
2631
|
appendEnvGroup(updatedLines, "# DeepSeek provider settings", grouped.deepseek);
|
|
2632
|
+
appendEnvGroup(updatedLines, "# Kimi provider settings", grouped.kimi);
|
|
2500
2633
|
appendEnvGroup(updatedLines, "# Shared model runtime settings", grouped.shared);
|
|
2501
2634
|
}
|
|
2502
2635
|
return `${updatedLines.join("\n").replace(/\n*$/u, "")}\n`;
|
|
@@ -2506,6 +2639,7 @@ function groupLoginEnvEntries(entries) {
|
|
|
2506
2639
|
active: entries.filter(([key]) => key === "MODEL_PROVIDER"),
|
|
2507
2640
|
openai: entries.filter(([key]) => key.startsWith("OPENAI_")),
|
|
2508
2641
|
deepseek: entries.filter(([key]) => key.startsWith("DEEPSEEK_")),
|
|
2642
|
+
kimi: entries.filter(([key]) => key.startsWith("KIMI_") || key.startsWith("MOONSHOT_")),
|
|
2509
2643
|
shared: entries.filter(([key]) => key.startsWith("MODEL_") && key !== "MODEL_PROVIDER"),
|
|
2510
2644
|
};
|
|
2511
2645
|
}
|
|
@@ -2639,7 +2773,7 @@ function kindForRole(role) {
|
|
|
2639
2773
|
}
|
|
2640
2774
|
function titleForKind(kind) {
|
|
2641
2775
|
if (kind === "thinking")
|
|
2642
|
-
return `${THINKING_MARKER}
|
|
2776
|
+
return `${THINKING_MARKER} think`;
|
|
2643
2777
|
if (kind === "tool")
|
|
2644
2778
|
return "Tool";
|
|
2645
2779
|
if (kind === "error")
|
|
@@ -2693,6 +2827,7 @@ function formatToolUse(toolUse) {
|
|
|
2693
2827
|
return {
|
|
2694
2828
|
kind: "tool",
|
|
2695
2829
|
title: toolTitle(toolUse.name, "running"),
|
|
2830
|
+
bodyTitle: planToolBodyTitle(toolUse.input),
|
|
2696
2831
|
text: formatPlanToolPayload(toolUse.input),
|
|
2697
2832
|
};
|
|
2698
2833
|
}
|
|
@@ -2708,6 +2843,7 @@ function formatToolResultLine(toolName, output, ok) {
|
|
|
2708
2843
|
const line = {
|
|
2709
2844
|
kind: ok ? "tool" : "error",
|
|
2710
2845
|
title: toolTitle(toolName, "finished"),
|
|
2846
|
+
bodyTitle: formatted.bodyTitle,
|
|
2711
2847
|
titleStatus: ok ? "success" : "failure",
|
|
2712
2848
|
text: formatted.text,
|
|
2713
2849
|
format: formatted.format,
|
|
@@ -2749,10 +2885,12 @@ function isPlanToolPayload(value) {
|
|
|
2749
2885
|
(item.status === "pending" || item.status === "in_progress" || item.status === "completed"));
|
|
2750
2886
|
});
|
|
2751
2887
|
}
|
|
2888
|
+
function planToolBodyTitle(payload) {
|
|
2889
|
+
const title = payload.title?.trim();
|
|
2890
|
+
return title ? title : undefined;
|
|
2891
|
+
}
|
|
2752
2892
|
function formatPlanToolPayload(payload) {
|
|
2753
2893
|
const sections = [];
|
|
2754
|
-
if (payload.title?.trim())
|
|
2755
|
-
sections.push(`**${payload.title.trim()}**`);
|
|
2756
2894
|
if (payload.summary?.trim())
|
|
2757
2895
|
sections.push(payload.summary.trim());
|
|
2758
2896
|
if (payload.note?.trim())
|
|
@@ -2877,7 +3015,7 @@ function formatToolResult(toolName, output, ok) {
|
|
|
2877
3015
|
return { text: formatWebSearchToolResult(output, ok), summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
|
|
2878
3016
|
}
|
|
2879
3017
|
if (toolName === "plan" && isPlanToolPayload(output)) {
|
|
2880
|
-
return { text: formatPlanToolPayload(output), full: true };
|
|
3018
|
+
return { text: formatPlanToolPayload(output), bodyTitle: planToolBodyTitle(output), full: true };
|
|
2881
3019
|
}
|
|
2882
3020
|
return { text: `${ok ? "ok" : "failed"}\n${formatJson(output, 6000)}`, summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
|
|
2883
3021
|
}
|