code-ollama 0.23.1 → 0.23.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/assets/{tui-iewVFcZW.js → tui-DEmaVgHT.js} +347 -124
- package/dist/cli.js +2 -2
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@ import { existsSync, readdirSync, statSync } from "node:fs";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
|
-
import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
|
|
7
|
-
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { Box, Static, Text, render, useApp, useInput, usePaste, useStdout } from "ink";
|
|
7
|
+
import { memo, useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { ProgressBar, Select, Spinner } from "@inkjs/ui";
|
|
10
10
|
import { Marked } from "marked";
|
|
@@ -747,36 +747,46 @@ function ToolApproval({ toolCall, onDecision, theme = getTheme() }) {
|
|
|
747
747
|
//#region src/components/TextInput/TextInput.tsx
|
|
748
748
|
function buildLineSegments(displayValue, cursorPosition, width) {
|
|
749
749
|
const safeWidth = Math.max(1, width);
|
|
750
|
-
const cursorChar = displayValue[cursorPosition] || " ";
|
|
751
|
-
const renderValue = displayValue.slice(0, cursorPosition) + cursorChar + displayValue.slice(cursorPosition + 1);
|
|
752
|
-
const totalLength = Math.max(1, renderValue.length);
|
|
753
750
|
const lines = [];
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
const
|
|
758
|
-
|
|
751
|
+
const logicalLines = displayValue.split("\n");
|
|
752
|
+
let lineStart = 0;
|
|
753
|
+
for (const [lineIndex, logicalLine] of logicalLines.entries()) {
|
|
754
|
+
const lineEnd = lineStart + logicalLine.length;
|
|
755
|
+
const hasCursorOnLine = cursorPosition >= lineStart && cursorPosition <= lineEnd;
|
|
756
|
+
const cursorOffset = cursorPosition - lineStart;
|
|
757
|
+
const renderValue = hasCursorOnLine && cursorOffset === logicalLine.length ? `${logicalLine} ` : logicalLine;
|
|
758
|
+
const totalLength = Math.max(1, renderValue.length);
|
|
759
|
+
for (let start = 0; start < totalLength; start += safeWidth) {
|
|
760
|
+
const end = start + safeWidth;
|
|
761
|
+
const text = renderValue.slice(start, end);
|
|
762
|
+
const hasCursor = hasCursorOnLine && cursorOffset >= start && cursorOffset < end;
|
|
763
|
+
if (!hasCursor) {
|
|
764
|
+
lines.push({
|
|
765
|
+
text,
|
|
766
|
+
hasCursor,
|
|
767
|
+
beforeCursor: "",
|
|
768
|
+
cursorChar: " ",
|
|
769
|
+
afterCursor: ""
|
|
770
|
+
});
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
const offset = cursorOffset - start;
|
|
759
774
|
lines.push({
|
|
760
775
|
text,
|
|
761
776
|
hasCursor,
|
|
762
|
-
beforeCursor:
|
|
763
|
-
cursorChar:
|
|
764
|
-
afterCursor:
|
|
777
|
+
beforeCursor: text.slice(0, offset),
|
|
778
|
+
cursorChar: text[offset],
|
|
779
|
+
afterCursor: text.slice(offset + 1)
|
|
765
780
|
});
|
|
766
|
-
continue;
|
|
767
781
|
}
|
|
768
|
-
|
|
769
|
-
lines.push({
|
|
770
|
-
text,
|
|
771
|
-
hasCursor,
|
|
772
|
-
beforeCursor: text.slice(0, offset),
|
|
773
|
-
cursorChar: text[offset] || " ",
|
|
774
|
-
afterCursor: text.slice(offset + 1)
|
|
775
|
-
});
|
|
782
|
+
lineStart = lineEnd + (lineIndex < logicalLines.length - 1 ? 1 : 0);
|
|
776
783
|
}
|
|
777
784
|
return lines;
|
|
778
785
|
}
|
|
779
|
-
function
|
|
786
|
+
function normalizePastedText(input) {
|
|
787
|
+
return input.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
788
|
+
}
|
|
789
|
+
function TextInput({ value, isDisabled = false, placeholder, cursorPosition: externalCursorPosition, allowMultilinePaste = false, wrapIndent = 0, onChange, onSubmit }) {
|
|
780
790
|
const { stdout } = useStdout();
|
|
781
791
|
const [cursorPosition, setCursorPosition] = useState(value.length);
|
|
782
792
|
const prevValueRef = useRef(value);
|
|
@@ -798,9 +808,24 @@ function TextInput({ value, isDisabled = false, placeholder, cursorPosition: ext
|
|
|
798
808
|
cursorPosition,
|
|
799
809
|
externalCursorPosition
|
|
800
810
|
]);
|
|
811
|
+
const insertText = useCallback((text) => {
|
|
812
|
+
onChange(value.slice(0, cursorPosition) + text + value.slice(cursorPosition));
|
|
813
|
+
setCursorPosition(cursorPosition + text.length);
|
|
814
|
+
}, [
|
|
815
|
+
cursorPosition,
|
|
816
|
+
onChange,
|
|
817
|
+
value
|
|
818
|
+
]);
|
|
819
|
+
usePaste((text) => {
|
|
820
|
+
insertText(normalizePastedText(text));
|
|
821
|
+
}, { isActive: allowMultilinePaste && !isDisabled });
|
|
801
822
|
useInput((input, key) => {
|
|
802
823
|
// v8 ignore next
|
|
803
824
|
if (isDisabled) return;
|
|
825
|
+
if (allowMultilinePaste && input.length > 1 && /[\r\n]/.test(input)) {
|
|
826
|
+
insertText(normalizePastedText(input));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
804
829
|
if (key.return) {
|
|
805
830
|
onSubmit(value);
|
|
806
831
|
setCursorPosition(0);
|
|
@@ -845,10 +870,7 @@ function TextInput({ value, isDisabled = false, placeholder, cursorPosition: ext
|
|
|
845
870
|
}
|
|
846
871
|
if (key.ctrl) return;
|
|
847
872
|
// v8 ignore start
|
|
848
|
-
if (input)
|
|
849
|
-
onChange(value.slice(0, cursorPosition) + input + value.slice(cursorPosition));
|
|
850
|
-
setCursorPosition(cursorPosition + input.length);
|
|
851
|
-
}
|
|
873
|
+
if (input) insertText(input);
|
|
852
874
|
// v8 ignore stop
|
|
853
875
|
}, { isActive: !isDisabled });
|
|
854
876
|
const displayValue = value || (placeholder ?? "");
|
|
@@ -948,25 +970,6 @@ function extractImageAttachments(input) {
|
|
|
948
970
|
};
|
|
949
971
|
}
|
|
950
972
|
//#endregion
|
|
951
|
-
//#region src/components/Chat/CommandMenu.tsx
|
|
952
|
-
function getMatchingCommands(input) {
|
|
953
|
-
const normalizedInput = input.trim().toLowerCase();
|
|
954
|
-
if (!normalizedInput.startsWith("/")) return [];
|
|
955
|
-
return LIST.filter(({ name }) => name.toLowerCase().startsWith(normalizedInput)).map(({ name, description }) => ({
|
|
956
|
-
label: `${name} - ${description}`,
|
|
957
|
-
value: name
|
|
958
|
-
}));
|
|
959
|
-
}
|
|
960
|
-
function CommandMenu({ input, onSubmit }) {
|
|
961
|
-
const commandOptions = useMemo(() => getMatchingCommands(input), [input]);
|
|
962
|
-
if (!commandOptions.length) return null;
|
|
963
|
-
return /* @__PURE__ */ jsx(SelectPrompt, {
|
|
964
|
-
highlightText: input,
|
|
965
|
-
onChange: onSubmit,
|
|
966
|
-
options: commandOptions
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
//#endregion
|
|
970
973
|
//#region src/components/Suggestions/Suggestions.tsx
|
|
971
974
|
var DEFAULT_MAX_VISIBLE_OPTIONS = 5;
|
|
972
975
|
function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_MAX_VISIBLE_OPTIONS, resetKey, onHighlight, onSelect }) {
|
|
@@ -999,7 +1002,7 @@ function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_
|
|
|
999
1002
|
setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
|
|
1000
1003
|
return;
|
|
1001
1004
|
}
|
|
1002
|
-
if (key.tab || key.return
|
|
1005
|
+
if (key.tab || key.return) onSelect(options[focusedIndex]);
|
|
1003
1006
|
});
|
|
1004
1007
|
if (!options.length) return null;
|
|
1005
1008
|
const visibleStart = Math.min(Math.max(0, focusedIndex - maxVisibleOptions + 1), Math.max(0, options.length - maxVisibleOptions));
|
|
@@ -1017,6 +1020,26 @@ function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_
|
|
|
1017
1020
|
});
|
|
1018
1021
|
}
|
|
1019
1022
|
//#endregion
|
|
1023
|
+
//#region src/components/Chat/CommandMenu.tsx
|
|
1024
|
+
function getMatchingCommands(input) {
|
|
1025
|
+
const normalizedInput = input.trim().toLowerCase();
|
|
1026
|
+
if (!normalizedInput.startsWith("/")) return [];
|
|
1027
|
+
return LIST.filter(({ name }) => name.toLowerCase().startsWith(normalizedInput)).map(({ name, description }) => ({
|
|
1028
|
+
label: `${name} - ${description}`,
|
|
1029
|
+
value: name
|
|
1030
|
+
}));
|
|
1031
|
+
}
|
|
1032
|
+
function CommandMenu({ input, onSubmit }) {
|
|
1033
|
+
const commandOptions = useMemo(() => getMatchingCommands(input), [input]);
|
|
1034
|
+
if (!commandOptions.length) return null;
|
|
1035
|
+
return /* @__PURE__ */ jsx(Suggestions, {
|
|
1036
|
+
onSelect: (option) => {
|
|
1037
|
+
onSubmit(option.value);
|
|
1038
|
+
},
|
|
1039
|
+
options: commandOptions
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
//#endregion
|
|
1020
1043
|
//#region src/components/Chat/FileSuggestions.tsx
|
|
1021
1044
|
var MENTION_PATTERN = /(^|.)@(\S+)/;
|
|
1022
1045
|
var RIPGREP_MAX_BUFFER = 10 * 1024 * 1024;
|
|
@@ -1263,7 +1286,8 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
|
|
|
1263
1286
|
onSubmit,
|
|
1264
1287
|
resetInput
|
|
1265
1288
|
]);
|
|
1266
|
-
const
|
|
1289
|
+
const isMultilineInput = input.includes("\n");
|
|
1290
|
+
const showCommandMenu = input.startsWith("/") && !isMultilineInput;
|
|
1267
1291
|
const showFileSuggestions = !showCommandMenu && hasFileSuggestionQuery(input);
|
|
1268
1292
|
const handleHistoryNavigation = useCallback((direction) => {
|
|
1269
1293
|
if (!history.length || showFileSuggestions || hasAttachments) return;
|
|
@@ -1305,7 +1329,7 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
|
|
|
1305
1329
|
showFileSuggestions
|
|
1306
1330
|
]);
|
|
1307
1331
|
const handleSubmitText = useCallback((value) => {
|
|
1308
|
-
if (value.startsWith("/")) return;
|
|
1332
|
+
if (value.startsWith("/") && !value.includes("\n")) return;
|
|
1309
1333
|
if (hasFileSuggestionQuery(value)) {
|
|
1310
1334
|
if (fileSuggestionRef.current) handleSelectFileSuggestion(fileSuggestionRef.current);
|
|
1311
1335
|
return;
|
|
@@ -1365,6 +1389,7 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
|
|
|
1365
1389
|
value: input,
|
|
1366
1390
|
isDisabled,
|
|
1367
1391
|
cursorPosition,
|
|
1392
|
+
allowMultilinePaste: true,
|
|
1368
1393
|
wrapIndent,
|
|
1369
1394
|
onChange: handleInputChange,
|
|
1370
1395
|
onSubmit: handleSubmitText,
|
|
@@ -1389,6 +1414,21 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
|
|
|
1389
1414
|
var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
|
|
1390
1415
|
var PLAN_CHECKLIST_REMINDER = "Then display the plan using either the Plan Needs Input or Proposed Plan Markdown template";
|
|
1391
1416
|
var PLAN_EXECUTION_REMINDER = `Do not claim success and do not call ${Array.from(WRITE_TOOLS).join(", ")} until the user approves execution`;
|
|
1417
|
+
var ChatActionType = /* @__PURE__ */ function(ChatActionType) {
|
|
1418
|
+
ChatActionType["AppendMessage"] = "append-message";
|
|
1419
|
+
ChatActionType["ClearPendingPlan"] = "clear-pending-plan";
|
|
1420
|
+
ChatActionType["ClearPendingToolCall"] = "clear-pending-tool-call";
|
|
1421
|
+
ChatActionType["CommitMessages"] = "commit-messages";
|
|
1422
|
+
ChatActionType["Interrupt"] = "interrupt";
|
|
1423
|
+
ChatActionType["RequestPlanReview"] = "request-plan-review";
|
|
1424
|
+
ChatActionType["RequestToolApproval"] = "request-tool-approval";
|
|
1425
|
+
ChatActionType["ResetSession"] = "reset-session";
|
|
1426
|
+
ChatActionType["SetLoading"] = "set-loading";
|
|
1427
|
+
ChatActionType["SetStreamingMessage"] = "set-streaming-message";
|
|
1428
|
+
ChatActionType["StartTurn"] = "start-turn";
|
|
1429
|
+
ChatActionType["ToolRejected"] = "tool-rejected";
|
|
1430
|
+
return ChatActionType;
|
|
1431
|
+
}({});
|
|
1392
1432
|
var InterruptReason = /* @__PURE__ */ function(InterruptReason) {
|
|
1393
1433
|
InterruptReason["Interrupted"] = "interrupted";
|
|
1394
1434
|
InterruptReason["Rejected"] = "rejected";
|
|
@@ -1406,27 +1446,91 @@ function hasExecutablePlan(content) {
|
|
|
1406
1446
|
return lines.slice(executionStepsIndex + 1, nextSectionIndex === -1 ? void 0 : nextSectionIndex).some((line) => /^(?:[-*]|\d+[.)])\s+\S/.test(line.trim()));
|
|
1407
1447
|
}
|
|
1408
1448
|
//#endregion
|
|
1449
|
+
//#region src/components/Chat/reducer.ts
|
|
1450
|
+
function createInitialChatState(messages = []) {
|
|
1451
|
+
return {
|
|
1452
|
+
messages,
|
|
1453
|
+
streamingMessage: null,
|
|
1454
|
+
isLoading: false,
|
|
1455
|
+
pendingToolCall: null,
|
|
1456
|
+
pendingPlan: null,
|
|
1457
|
+
interruptReason: null
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
function chatReducer(state, action) {
|
|
1461
|
+
switch (action.type) {
|
|
1462
|
+
case ChatActionType.AppendMessage: return {
|
|
1463
|
+
...state,
|
|
1464
|
+
messages: [...state.messages, action.message]
|
|
1465
|
+
};
|
|
1466
|
+
case ChatActionType.ClearPendingPlan: return {
|
|
1467
|
+
...state,
|
|
1468
|
+
pendingPlan: null
|
|
1469
|
+
};
|
|
1470
|
+
case ChatActionType.ClearPendingToolCall: return {
|
|
1471
|
+
...state,
|
|
1472
|
+
pendingToolCall: null
|
|
1473
|
+
};
|
|
1474
|
+
case ChatActionType.CommitMessages: return {
|
|
1475
|
+
...state,
|
|
1476
|
+
messages: action.messages
|
|
1477
|
+
};
|
|
1478
|
+
case ChatActionType.Interrupt: return {
|
|
1479
|
+
...state,
|
|
1480
|
+
messages: [...state.messages, action.message],
|
|
1481
|
+
streamingMessage: null,
|
|
1482
|
+
isLoading: false,
|
|
1483
|
+
interruptReason: InterruptReason.Interrupted
|
|
1484
|
+
};
|
|
1485
|
+
case ChatActionType.RequestPlanReview: return {
|
|
1486
|
+
...state,
|
|
1487
|
+
pendingPlan: action.pendingPlan,
|
|
1488
|
+
isLoading: false
|
|
1489
|
+
};
|
|
1490
|
+
case ChatActionType.RequestToolApproval: return {
|
|
1491
|
+
...state,
|
|
1492
|
+
pendingToolCall: action.pendingToolCall,
|
|
1493
|
+
isLoading: false
|
|
1494
|
+
};
|
|
1495
|
+
case ChatActionType.ResetSession: return createInitialChatState(action.messages);
|
|
1496
|
+
case ChatActionType.SetLoading: return {
|
|
1497
|
+
...state,
|
|
1498
|
+
isLoading: action.isLoading
|
|
1499
|
+
};
|
|
1500
|
+
case ChatActionType.SetStreamingMessage: return {
|
|
1501
|
+
...state,
|
|
1502
|
+
streamingMessage: action.message
|
|
1503
|
+
};
|
|
1504
|
+
case ChatActionType.StartTurn: return {
|
|
1505
|
+
...state,
|
|
1506
|
+
messages: [...state.messages, action.message],
|
|
1507
|
+
isLoading: true,
|
|
1508
|
+
interruptReason: null
|
|
1509
|
+
};
|
|
1510
|
+
case ChatActionType.ToolRejected: return {
|
|
1511
|
+
...state,
|
|
1512
|
+
messages: action.messages,
|
|
1513
|
+
isLoading: false,
|
|
1514
|
+
interruptReason: InterruptReason.Rejected
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
//#endregion
|
|
1409
1519
|
//#region src/components/Chat/Chat.tsx
|
|
1410
1520
|
var MAX_TOOL_TURNS = 25;
|
|
1411
1521
|
var MAX_TOOL_INTENT_CORRECTIONS = 2;
|
|
1412
1522
|
function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onModeChange, sessionId, theme = getTheme() }) {
|
|
1413
1523
|
const sessionMessages = initialMessages ?? [];
|
|
1414
1524
|
const history = useMemo(() => sessionMessages.flatMap(({ role, content }) => role === "user" && !content.startsWith("/") ? [content] : []), [sessionMessages]);
|
|
1415
|
-
const [
|
|
1416
|
-
const
|
|
1417
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
1418
|
-
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
1419
|
-
const [pendingPlan, setPendingPlan] = useState(null);
|
|
1420
|
-
const [interruptReason, setInterruptReason] = useState(null);
|
|
1525
|
+
const [state, dispatch] = useReducer(chatReducer, sessionMessages, createInitialChatState);
|
|
1526
|
+
const { messages, streamingMessage, isLoading, pendingToolCall, pendingPlan, interruptReason } = state;
|
|
1421
1527
|
const abortControllerRef = useRef(null);
|
|
1422
1528
|
const persistedSnapshotRef = useRef("");
|
|
1423
1529
|
useEffect(() => {
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
setPendingPlan(null);
|
|
1429
|
-
setInterruptReason(null);
|
|
1530
|
+
dispatch({
|
|
1531
|
+
type: ChatActionType.ResetSession,
|
|
1532
|
+
messages: sessionMessages
|
|
1533
|
+
});
|
|
1430
1534
|
persistedSnapshotRef.current = JSON.stringify(sessionMessages);
|
|
1431
1535
|
}, [sessionId]);
|
|
1432
1536
|
useEffect(() => {
|
|
@@ -1467,13 +1571,13 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1467
1571
|
const handleInterrupt = useCallback(() => {
|
|
1468
1572
|
abortControllerRef.current?.abort();
|
|
1469
1573
|
abortControllerRef.current = null;
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
}
|
|
1574
|
+
dispatch({
|
|
1575
|
+
type: ChatActionType.Interrupt,
|
|
1576
|
+
message: {
|
|
1577
|
+
role: USER,
|
|
1578
|
+
content: TURN_ABORTED_MESSAGE
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1477
1581
|
}, []);
|
|
1478
1582
|
const processStream = useCallback(async (currentMessages, executionMode = mode) => {
|
|
1479
1583
|
const modelName = model;
|
|
@@ -1498,27 +1602,45 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1498
1602
|
if (assistantCommitted) {
|
|
1499
1603
|
if (committedMessages.at(-1)?.role === "assistant") {
|
|
1500
1604
|
committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
|
|
1501
|
-
|
|
1605
|
+
dispatch({
|
|
1606
|
+
type: ChatActionType.CommitMessages,
|
|
1607
|
+
messages: committedMessages
|
|
1608
|
+
});
|
|
1502
1609
|
}
|
|
1503
1610
|
return committedMessages;
|
|
1504
1611
|
}
|
|
1505
1612
|
// v8 ignore stop
|
|
1506
1613
|
assistantCommitted = true;
|
|
1507
|
-
|
|
1614
|
+
dispatch({
|
|
1615
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1616
|
+
message: null
|
|
1617
|
+
});
|
|
1508
1618
|
if (!assistantMessage.content) {
|
|
1509
|
-
|
|
1619
|
+
dispatch({
|
|
1620
|
+
type: ChatActionType.CommitMessages,
|
|
1621
|
+
messages: committedMessages
|
|
1622
|
+
});
|
|
1510
1623
|
return committedMessages;
|
|
1511
1624
|
}
|
|
1512
1625
|
committedMessages = [...committedMessages, { ...assistantMessage }];
|
|
1513
|
-
|
|
1626
|
+
dispatch({
|
|
1627
|
+
type: ChatActionType.CommitMessages,
|
|
1628
|
+
messages: committedMessages
|
|
1629
|
+
});
|
|
1514
1630
|
return committedMessages;
|
|
1515
1631
|
};
|
|
1516
|
-
|
|
1632
|
+
dispatch({
|
|
1633
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1634
|
+
message: assistantMessage
|
|
1635
|
+
});
|
|
1517
1636
|
let nextMessages = null;
|
|
1518
1637
|
for await (const chunk of streamChat(withSystemMessage(activeMessages), modelName, TOOLS, controller.signal)) {
|
|
1519
1638
|
if (chunk.type === "content") {
|
|
1520
1639
|
assistantMessage.content = sanitizeAssistantContent(assistantMessage.content + chunk.content);
|
|
1521
|
-
|
|
1640
|
+
dispatch({
|
|
1641
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1642
|
+
message: { ...assistantMessage }
|
|
1643
|
+
});
|
|
1522
1644
|
continue;
|
|
1523
1645
|
}
|
|
1524
1646
|
if (chunk.tool_calls.length === 0) continue;
|
|
@@ -1527,12 +1649,14 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1527
1649
|
for (const toolCall of chunk.tool_calls) try {
|
|
1528
1650
|
const normalized = normalizeToolCall(toolCall);
|
|
1529
1651
|
if (executionMode === "safe" && normalized.requiresApproval) {
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1652
|
+
dispatch({
|
|
1653
|
+
type: ChatActionType.RequestToolApproval,
|
|
1654
|
+
pendingToolCall: {
|
|
1655
|
+
toolCall,
|
|
1656
|
+
messages: [...updatedMessages, ...toolResultMessages],
|
|
1657
|
+
executionMode
|
|
1658
|
+
}
|
|
1534
1659
|
});
|
|
1535
|
-
setIsLoading(false);
|
|
1536
1660
|
return;
|
|
1537
1661
|
}
|
|
1538
1662
|
// v8 ignore next
|
|
@@ -1547,7 +1671,10 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1547
1671
|
}));
|
|
1548
1672
|
}
|
|
1549
1673
|
nextMessages = [...updatedMessages, ...toolResultMessages];
|
|
1550
|
-
|
|
1674
|
+
dispatch({
|
|
1675
|
+
type: ChatActionType.CommitMessages,
|
|
1676
|
+
messages: nextMessages
|
|
1677
|
+
});
|
|
1551
1678
|
break;
|
|
1552
1679
|
}
|
|
1553
1680
|
if (!nextMessages) {
|
|
@@ -1559,7 +1686,10 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1559
1686
|
role: SYSTEM,
|
|
1560
1687
|
content: TOOL_INTENT_CORRECTION
|
|
1561
1688
|
}];
|
|
1562
|
-
|
|
1689
|
+
dispatch({
|
|
1690
|
+
type: ChatActionType.CommitMessages,
|
|
1691
|
+
messages: activeMessages
|
|
1692
|
+
});
|
|
1563
1693
|
continue;
|
|
1564
1694
|
}
|
|
1565
1695
|
return;
|
|
@@ -1568,14 +1698,18 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1568
1698
|
toolIntentCorrections = 0;
|
|
1569
1699
|
/* v8 ignore start */
|
|
1570
1700
|
if (toolTurns >= MAX_TOOL_TURNS) {
|
|
1571
|
-
|
|
1701
|
+
const stoppedMessages = [...nextMessages, {
|
|
1572
1702
|
role: SYSTEM,
|
|
1573
1703
|
content: [
|
|
1574
1704
|
"Tool execution stopped because the maximum tool turn limit was reached",
|
|
1575
1705
|
ACTION_NOT_PERFORMED,
|
|
1576
1706
|
"Summarize completed work and explain what remains without calling more tools."
|
|
1577
1707
|
].join("\n")
|
|
1578
|
-
}]
|
|
1708
|
+
}];
|
|
1709
|
+
dispatch({
|
|
1710
|
+
type: ChatActionType.CommitMessages,
|
|
1711
|
+
messages: stoppedMessages
|
|
1712
|
+
});
|
|
1579
1713
|
return;
|
|
1580
1714
|
}
|
|
1581
1715
|
/* v8 ignore stop */
|
|
@@ -1589,13 +1723,22 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1589
1723
|
content: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1590
1724
|
};
|
|
1591
1725
|
await prewarmCodeBlocks(errorMessage.content, theme);
|
|
1592
|
-
|
|
1593
|
-
|
|
1726
|
+
dispatch({
|
|
1727
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1728
|
+
message: null
|
|
1729
|
+
});
|
|
1730
|
+
dispatch({
|
|
1731
|
+
type: ChatActionType.CommitMessages,
|
|
1732
|
+
messages: [...activeMessages, errorMessage]
|
|
1733
|
+
});
|
|
1594
1734
|
}
|
|
1595
1735
|
} finally {
|
|
1596
1736
|
// v8 ignore next
|
|
1597
1737
|
if (abortControllerRef.current === controller) abortControllerRef.current = null;
|
|
1598
|
-
|
|
1738
|
+
dispatch({
|
|
1739
|
+
type: ChatActionType.SetLoading,
|
|
1740
|
+
isLoading: false
|
|
1741
|
+
});
|
|
1599
1742
|
}
|
|
1600
1743
|
}, [
|
|
1601
1744
|
buildToolResultMessage,
|
|
@@ -1625,22 +1768,37 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1625
1768
|
if (assistantCommitted) {
|
|
1626
1769
|
if (committedMessages.at(-1)?.role === "assistant") {
|
|
1627
1770
|
committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
|
|
1628
|
-
|
|
1771
|
+
dispatch({
|
|
1772
|
+
type: ChatActionType.CommitMessages,
|
|
1773
|
+
messages: committedMessages
|
|
1774
|
+
});
|
|
1629
1775
|
}
|
|
1630
1776
|
return committedMessages;
|
|
1631
1777
|
}
|
|
1632
1778
|
/* v8 ignore stop */
|
|
1633
1779
|
assistantCommitted = true;
|
|
1634
|
-
|
|
1780
|
+
dispatch({
|
|
1781
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1782
|
+
message: null
|
|
1783
|
+
});
|
|
1635
1784
|
if (!assistantMessage.content) {
|
|
1636
|
-
|
|
1785
|
+
dispatch({
|
|
1786
|
+
type: ChatActionType.CommitMessages,
|
|
1787
|
+
messages: committedMessages
|
|
1788
|
+
});
|
|
1637
1789
|
return committedMessages;
|
|
1638
1790
|
}
|
|
1639
1791
|
committedMessages = [...committedMessages, { ...assistantMessage }];
|
|
1640
|
-
|
|
1792
|
+
dispatch({
|
|
1793
|
+
type: ChatActionType.CommitMessages,
|
|
1794
|
+
messages: committedMessages
|
|
1795
|
+
});
|
|
1641
1796
|
return committedMessages;
|
|
1642
1797
|
};
|
|
1643
|
-
|
|
1798
|
+
dispatch({
|
|
1799
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1800
|
+
message: emptyAssistantMessage
|
|
1801
|
+
});
|
|
1644
1802
|
try {
|
|
1645
1803
|
const readOnlyTools = TOOLS.filter((tool) => READ_TOOLS.has(tool.function.name));
|
|
1646
1804
|
const planResearchMessages = [...currentMessages, {
|
|
@@ -1652,18 +1810,30 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1652
1810
|
if (controller.signal.aborted) return;
|
|
1653
1811
|
if (chunk.type === "content") {
|
|
1654
1812
|
assistantMessage.content = sanitizeAssistantContent(assistantMessage.content + chunk.content);
|
|
1655
|
-
|
|
1813
|
+
dispatch({
|
|
1814
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1815
|
+
message: { ...assistantMessage }
|
|
1816
|
+
});
|
|
1656
1817
|
} else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
|
|
1657
1818
|
const toolName = toolCall.function.name;
|
|
1658
1819
|
if (!READ_TOOLS.has(toolName)) {
|
|
1659
1820
|
const correctionMessage = buildPlanModeCorrectionMessage(toolName);
|
|
1660
|
-
|
|
1821
|
+
dispatch({
|
|
1822
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1823
|
+
message: null
|
|
1824
|
+
});
|
|
1661
1825
|
const newMessages = [...committedMessages, correctionMessage];
|
|
1662
|
-
|
|
1826
|
+
dispatch({
|
|
1827
|
+
type: ChatActionType.CommitMessages,
|
|
1828
|
+
messages: newMessages
|
|
1829
|
+
});
|
|
1663
1830
|
await processStreamReadOnly(newMessages);
|
|
1664
1831
|
return;
|
|
1665
1832
|
}
|
|
1666
|
-
|
|
1833
|
+
dispatch({
|
|
1834
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1835
|
+
message: emptyAssistantMessage
|
|
1836
|
+
});
|
|
1667
1837
|
assistantMessage.content = "";
|
|
1668
1838
|
const updatedMessages = committedMessages;
|
|
1669
1839
|
let normalized;
|
|
@@ -1676,14 +1846,20 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1676
1846
|
error: error instanceof Error ? error.message : String(error)
|
|
1677
1847
|
});
|
|
1678
1848
|
const newMessages = [...updatedMessages, toolResultMessage];
|
|
1679
|
-
|
|
1849
|
+
dispatch({
|
|
1850
|
+
type: ChatActionType.CommitMessages,
|
|
1851
|
+
messages: newMessages
|
|
1852
|
+
});
|
|
1680
1853
|
await processStreamReadOnly(newMessages);
|
|
1681
1854
|
return;
|
|
1682
1855
|
}
|
|
1683
1856
|
const result = await executeTool(normalized.name, normalized.arguments, { allowedTools: READ_TOOLS });
|
|
1684
1857
|
const toolResultMessage = buildToolResultMessage(normalized.name, result, normalized.arguments);
|
|
1685
1858
|
const newMessages = [...updatedMessages, toolResultMessage];
|
|
1686
|
-
|
|
1859
|
+
dispatch({
|
|
1860
|
+
type: ChatActionType.CommitMessages,
|
|
1861
|
+
messages: newMessages
|
|
1862
|
+
});
|
|
1687
1863
|
await processStreamReadOnly(newMessages);
|
|
1688
1864
|
return;
|
|
1689
1865
|
}
|
|
@@ -1691,11 +1867,13 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1691
1867
|
await prewarmCodeBlocks(assistantMessage.content, theme);
|
|
1692
1868
|
const researchMessages = commitAssistantMessage();
|
|
1693
1869
|
if (hasExecutablePlan(assistantMessage.content)) {
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1870
|
+
dispatch({
|
|
1871
|
+
type: ChatActionType.RequestPlanReview,
|
|
1872
|
+
pendingPlan: {
|
|
1873
|
+
planContent: assistantMessage.content,
|
|
1874
|
+
messages: researchMessages
|
|
1875
|
+
}
|
|
1697
1876
|
});
|
|
1698
|
-
setIsLoading(false);
|
|
1699
1877
|
return;
|
|
1700
1878
|
}
|
|
1701
1879
|
const planInstruction = {
|
|
@@ -1707,32 +1885,60 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1707
1885
|
role: ASSISTANT,
|
|
1708
1886
|
content: ""
|
|
1709
1887
|
};
|
|
1710
|
-
|
|
1888
|
+
dispatch({
|
|
1889
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1890
|
+
message: emptyAssistantMessage
|
|
1891
|
+
});
|
|
1711
1892
|
try {
|
|
1712
1893
|
for await (const chunk of streamChat(withSystemMessage(planMessages), modelName, [], controller.signal)) {
|
|
1713
1894
|
// v8 ignore next 3
|
|
1714
1895
|
if (controller.signal.aborted) return;
|
|
1715
1896
|
if (chunk.type === "content") {
|
|
1716
1897
|
planAssistantMessage.content = sanitizeAssistantContent(planAssistantMessage.content + chunk.content);
|
|
1717
|
-
|
|
1898
|
+
dispatch({
|
|
1899
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1900
|
+
message: { ...planAssistantMessage }
|
|
1901
|
+
});
|
|
1718
1902
|
}
|
|
1719
1903
|
}
|
|
1720
1904
|
} catch (error) {
|
|
1721
1905
|
// v8 ignore next
|
|
1722
1906
|
planAssistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1907
|
+
const errorPlanMessages = [...planMessages, { ...planAssistantMessage }];
|
|
1908
|
+
dispatch({
|
|
1909
|
+
type: ChatActionType.CommitMessages,
|
|
1910
|
+
messages: errorPlanMessages
|
|
1911
|
+
});
|
|
1912
|
+
dispatch({
|
|
1913
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1914
|
+
message: null
|
|
1915
|
+
});
|
|
1916
|
+
dispatch({
|
|
1917
|
+
type: ChatActionType.SetLoading,
|
|
1918
|
+
isLoading: false
|
|
1919
|
+
});
|
|
1726
1920
|
return;
|
|
1727
1921
|
}
|
|
1728
1922
|
const finalPlanMessages = [...planMessages, { ...planAssistantMessage }];
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
if (hasExecutablePlan(planAssistantMessage.content)) setPendingPlan({
|
|
1732
|
-
planContent: planAssistantMessage.content,
|
|
1923
|
+
dispatch({
|
|
1924
|
+
type: ChatActionType.CommitMessages,
|
|
1733
1925
|
messages: finalPlanMessages
|
|
1734
1926
|
});
|
|
1735
|
-
|
|
1927
|
+
dispatch({
|
|
1928
|
+
type: ChatActionType.SetStreamingMessage,
|
|
1929
|
+
message: null
|
|
1930
|
+
});
|
|
1931
|
+
if (hasExecutablePlan(planAssistantMessage.content)) dispatch({
|
|
1932
|
+
type: ChatActionType.RequestPlanReview,
|
|
1933
|
+
pendingPlan: {
|
|
1934
|
+
planContent: planAssistantMessage.content,
|
|
1935
|
+
messages: finalPlanMessages
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
dispatch({
|
|
1939
|
+
type: ChatActionType.SetLoading,
|
|
1940
|
+
isLoading: false
|
|
1941
|
+
});
|
|
1736
1942
|
} catch (error) {
|
|
1737
1943
|
// v8 ignore next
|
|
1738
1944
|
if (!controller.signal.aborted) {
|
|
@@ -1742,7 +1948,10 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1742
1948
|
}
|
|
1743
1949
|
} finally {
|
|
1744
1950
|
if (abortControllerRef.current === controller) abortControllerRef.current = null;
|
|
1745
|
-
|
|
1951
|
+
dispatch({
|
|
1952
|
+
type: ChatActionType.SetLoading,
|
|
1953
|
+
isLoading: false
|
|
1954
|
+
});
|
|
1746
1955
|
}
|
|
1747
1956
|
}, [
|
|
1748
1957
|
buildPlanModeCorrectionMessage,
|
|
@@ -1754,19 +1963,25 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1754
1963
|
// v8 ignore next
|
|
1755
1964
|
if (!pendingPlan) return;
|
|
1756
1965
|
const { messages: planMessages } = pendingPlan;
|
|
1757
|
-
|
|
1966
|
+
dispatch({ type: ChatActionType.ClearPendingPlan });
|
|
1758
1967
|
if (mode === "plan") {
|
|
1759
1968
|
onModeChange(PLAN);
|
|
1760
1969
|
const cancelMessage = {
|
|
1761
1970
|
role: SYSTEM,
|
|
1762
1971
|
content: "Continuing in Plan mode. No tools were executed."
|
|
1763
1972
|
};
|
|
1764
|
-
|
|
1973
|
+
dispatch({
|
|
1974
|
+
type: ChatActionType.AppendMessage,
|
|
1975
|
+
message: cancelMessage
|
|
1976
|
+
});
|
|
1765
1977
|
return;
|
|
1766
1978
|
}
|
|
1767
1979
|
const selectedMode = mode === "auto" ? AUTO : SAFE;
|
|
1768
1980
|
onModeChange(selectedMode);
|
|
1769
|
-
|
|
1981
|
+
dispatch({
|
|
1982
|
+
type: ChatActionType.SetLoading,
|
|
1983
|
+
isLoading: true
|
|
1984
|
+
});
|
|
1770
1985
|
const executeInstruction = {
|
|
1771
1986
|
role: SYSTEM,
|
|
1772
1987
|
content: mode === "auto" ? "Execute the plan above. Use tools as needed without asking for further confirmation." : "Execute the plan above one step at a time. Wait for user approval before each tool call that modifies files or runs commands."
|
|
@@ -1781,14 +1996,20 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1781
1996
|
// v8 ignore next
|
|
1782
1997
|
if (!pendingToolCall) return;
|
|
1783
1998
|
const { executionMode, messages: approvedMessages, toolCall } = pendingToolCall;
|
|
1784
|
-
|
|
1785
|
-
|
|
1999
|
+
dispatch({ type: ChatActionType.ClearPendingToolCall });
|
|
2000
|
+
dispatch({
|
|
2001
|
+
type: ChatActionType.SetLoading,
|
|
2002
|
+
isLoading: true
|
|
2003
|
+
});
|
|
1786
2004
|
switch (decision) {
|
|
1787
2005
|
case APPROVE: {
|
|
1788
2006
|
const result = await executeToolCall(toolCall);
|
|
1789
2007
|
const toolResultMessage = buildToolResultMessage(toolCall.function.name, result, toolCall.function.arguments);
|
|
1790
2008
|
const newMessages = [...approvedMessages, toolResultMessage];
|
|
1791
|
-
|
|
2009
|
+
dispatch({
|
|
2010
|
+
type: ChatActionType.CommitMessages,
|
|
2011
|
+
messages: newMessages
|
|
2012
|
+
});
|
|
1792
2013
|
await processStream(newMessages, executionMode);
|
|
1793
2014
|
break;
|
|
1794
2015
|
}
|
|
@@ -1800,9 +2021,10 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1800
2021
|
error: "Tool call rejected by user"
|
|
1801
2022
|
}, toolCall.function.arguments)
|
|
1802
2023
|
};
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
2024
|
+
dispatch({
|
|
2025
|
+
type: ChatActionType.ToolRejected,
|
|
2026
|
+
messages: [...approvedMessages, toolResultMessage]
|
|
2027
|
+
});
|
|
1806
2028
|
break;
|
|
1807
2029
|
}
|
|
1808
2030
|
}
|
|
@@ -1812,21 +2034,22 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1812
2034
|
processStream
|
|
1813
2035
|
]);
|
|
1814
2036
|
const handleSubmit = useCallback(async ({ content, images }) => {
|
|
1815
|
-
setInterruptReason(null);
|
|
1816
2037
|
const userContent = content.trim();
|
|
1817
2038
|
if (!userContent && !images?.length) return;
|
|
1818
2039
|
if (userContent.startsWith("/")) {
|
|
1819
2040
|
onCommand(userContent);
|
|
1820
2041
|
return;
|
|
1821
2042
|
}
|
|
1822
|
-
setIsLoading(true);
|
|
1823
2043
|
const userMessage = {
|
|
1824
2044
|
role: USER,
|
|
1825
2045
|
content: userContent,
|
|
1826
2046
|
...images?.length ? { images } : {}
|
|
1827
2047
|
};
|
|
1828
2048
|
const updatedMessages = [...messages, userMessage];
|
|
1829
|
-
|
|
2049
|
+
dispatch({
|
|
2050
|
+
type: ChatActionType.StartTurn,
|
|
2051
|
+
message: userMessage
|
|
2052
|
+
});
|
|
1830
2053
|
if (mode === "plan") await processStreamReadOnly(updatedMessages);
|
|
1831
2054
|
else await processStream(updatedMessages);
|
|
1832
2055
|
}, [
|
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var LIST$1 = [
|
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region package.json
|
|
52
52
|
var name = "code-ollama";
|
|
53
|
-
var version = "0.23.
|
|
53
|
+
var version = "0.23.2";
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/constants/package.ts
|
|
56
56
|
var NAME = name;
|
|
@@ -1582,7 +1582,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1582
1582
|
else await launchTui();
|
|
1583
1583
|
}
|
|
1584
1584
|
async function launchTui(sessionId) {
|
|
1585
|
-
const { renderApp } = await import("./assets/tui-
|
|
1585
|
+
const { renderApp } = await import("./assets/tui-DEmaVgHT.js");
|
|
1586
1586
|
reset();
|
|
1587
1587
|
renderApp(sessionId);
|
|
1588
1588
|
}
|