code-ollama 0.21.0 → 0.21.1

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 CHANGED
@@ -21,6 +21,9 @@
21
21
  npx code-ollama
22
22
  ```
23
23
 
24
+ > [!IMPORTANT]
25
+ > If you see an error that says server/model is unavailable, then follow these [steps](https://github.com/ai-action/code-ollama/wiki/Ollama).
26
+
24
27
  ## Install
25
28
 
26
29
  Install the [CLI](https://www.npmjs.com/package/code-ollama) globally:
@@ -1,4 +1,4 @@
1
- import { A as HEADER_PREFIX, B as AUTO, C as streamChat, D as saveClipboardImage, E as removeClipboardImage, F as SYSTEM, G as REJECT, H as PLAN, I as USER, J as LIST, K as NAME, L as PLAN_GENERATION_INSTRUCTION, M as LIST$1, N as getTheme, O as resetSystemMessage, P as ASSISTANT, R as BACK, S as pullModel, T as saveConfig, U as SAFE, V as LABEL, W as APPROVE, _ as reset, a as WRITE_TOOLS, b as deleteModel, c as write, d as deleteSession, f as deleteSessionIfEmpty, g as clear, h as updateSessionModel, i as TOOLS, j as WARNING, k as withSystemMessage, l as appendMessage, m as loadSession, n as executeTool, o as tick, p as listSessions, q as VERSION, r as READ_TOOLS, s as color, t as checkForUpdate, u as createSession, v as setClearHandler, w as loadConfig, x as listModels, y as checkHealth, z as CATALOG } from "../cli.js";
1
+ import { A as saveClipboardImage, B as PLAN_GENERATION_INSTRUCTION, C as deleteModel, D as loadConfig, E as streamChat, F as LIST$1, G as PLAN, H as CATALOG, I as getTheme, J as REJECT, K as SAFE, L as ASSISTANT, M as withSystemMessage, N as HEADER_PREFIX, O as saveConfig, P as WARNING, R as SYSTEM, S as checkHealth, T as pullModel, U as AUTO, V as BACK, W as LABEL, X as VERSION, Y as NAME, Z as LIST, _ as loadSession, a as normalizeToolCall, b as reset, c as WRITE_TOOLS, d as write, f as appendMessage, g as listSessions, h as deleteSessionIfEmpty, i as formatToolResultContent, j as resetSystemMessage, k as removeClipboardImage, l as tick, m as deleteSession, n as executeTool, o as READ_TOOLS, p as createSession, q as APPROVE, r as executeToolCall, s as TOOLS, t as checkForUpdate, u as color, v as updateSessionModel, w as listModels, x as setClearHandler, y as clear, z as USER } from "../cli.js";
2
2
  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";
@@ -1362,6 +1362,7 @@ function hasExecutablePlan(content) {
1362
1362
  }
1363
1363
  //#endregion
1364
1364
  //#region src/components/Chat/Chat.tsx
1365
+ var MAX_TOOL_TURNS = 25;
1365
1366
  function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onModeChange, sessionId, theme = getTheme() }) {
1366
1367
  const sessionMessages = initialMessages ?? [];
1367
1368
  const history = useMemo(() => sessionMessages.flatMap(({ role, content }) => role === "user" && !content.startsWith("/") ? [content] : []), [sessionMessages]);
@@ -1400,7 +1401,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1400
1401
  };
1401
1402
  return {
1402
1403
  role: SYSTEM,
1403
- content: `Tool ${toolName} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1404
+ content: formatToolResultContent(toolName, result)
1404
1405
  };
1405
1406
  }, []);
1406
1407
  const buildPlanModeCorrectionMessage = useCallback((toolName) => ({
@@ -1425,72 +1426,112 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1425
1426
  }]);
1426
1427
  }, []);
1427
1428
  const processStream = useCallback(async (currentMessages, executionMode = mode) => {
1429
+ const modelName = model;
1428
1430
  // v8 ignore next
1429
- if (!model) throw new Error("Model is required");
1431
+ if (!modelName) throw new Error("Model is required");
1430
1432
  const controller = new AbortController();
1431
1433
  abortControllerRef.current = controller;
1432
- const assistantMessage = {
1433
- role: ASSISTANT,
1434
- content: ""
1435
- };
1436
- let committedMessages = currentMessages;
1437
- let assistantCommitted = false;
1438
- const commitAssistantMessage = () => {
1439
- if (assistantCommitted) {
1440
- // v8 ignore next
1441
- if (committedMessages.at(-1)?.role === "assistant") {
1442
- committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
1443
- setMessages(committedMessages);
1444
- }
1445
- return committedMessages;
1446
- }
1447
- assistantCommitted = true;
1448
- setStreamingMessage(null);
1449
- if (!assistantMessage.content) {
1450
- setMessages(committedMessages);
1451
- return committedMessages;
1452
- }
1453
- committedMessages = [...committedMessages, { ...assistantMessage }];
1454
- setMessages(committedMessages);
1455
- return committedMessages;
1456
- };
1457
- setStreamingMessage(assistantMessage);
1434
+ let activeMessages = currentMessages;
1435
+ let toolTurns = 0;
1458
1436
  try {
1459
- for await (const chunk of streamChat(withSystemMessage(currentMessages), model, TOOLS, controller.signal)) {
1460
- // v8 ignore next 3
1461
- if (controller.signal.aborted) return;
1462
- if (chunk.type === "content") {
1463
- assistantMessage.content += chunk.content;
1464
- setStreamingMessage({ ...assistantMessage });
1465
- } else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
1466
- const requiresApproval = WRITE_TOOLS.has(toolCall.function.name);
1437
+ while (!controller.signal.aborted) {
1438
+ const assistantMessage = {
1439
+ role: ASSISTANT,
1440
+ content: ""
1441
+ };
1442
+ let committedMessages = activeMessages;
1443
+ let assistantCommitted = false;
1444
+ const commitAssistantMessage = () => {
1467
1445
  // v8 ignore start
1468
- const allowedTools = executionMode === "plan" ? READ_TOOLS : void 0;
1446
+ if (assistantCommitted) {
1447
+ if (committedMessages.at(-1)?.role === "assistant") {
1448
+ committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
1449
+ setMessages(committedMessages);
1450
+ }
1451
+ return committedMessages;
1452
+ }
1469
1453
  // v8 ignore stop
1454
+ assistantCommitted = true;
1455
+ setStreamingMessage(null);
1456
+ if (!assistantMessage.content) {
1457
+ setMessages(committedMessages);
1458
+ return committedMessages;
1459
+ }
1460
+ committedMessages = [...committedMessages, { ...assistantMessage }];
1461
+ setMessages(committedMessages);
1462
+ return committedMessages;
1463
+ };
1464
+ setStreamingMessage(assistantMessage);
1465
+ let nextMessages = null;
1466
+ for await (const chunk of streamChat(withSystemMessage(activeMessages), modelName, TOOLS, controller.signal)) {
1467
+ if (chunk.type === "content") {
1468
+ assistantMessage.content += chunk.content;
1469
+ setStreamingMessage({ ...assistantMessage });
1470
+ continue;
1471
+ }
1472
+ if (chunk.tool_calls.length === 0) continue;
1470
1473
  const updatedMessages = commitAssistantMessage();
1471
- if (executionMode === "safe" && requiresApproval) {
1472
- setPendingToolCall(toolCall);
1473
- setIsLoading(false);
1474
- return;
1474
+ const toolResultMessages = [];
1475
+ for (const toolCall of chunk.tool_calls) try {
1476
+ const normalized = normalizeToolCall(toolCall);
1477
+ if (executionMode === "safe" && normalized.requiresApproval) {
1478
+ setPendingToolCall({
1479
+ toolCall,
1480
+ messages: [...updatedMessages, ...toolResultMessages],
1481
+ executionMode
1482
+ });
1483
+ setIsLoading(false);
1484
+ return;
1485
+ }
1486
+ // v8 ignore next
1487
+ const allowedTools = executionMode === "plan" ? READ_TOOLS : void 0;
1488
+ const result = await executeTool(normalized.name, normalized.arguments, { allowedTools });
1489
+ toolResultMessages.push(buildToolResultMessage(normalized.name, result));
1490
+ } catch (error) {
1491
+ toolResultMessages.push(buildToolResultMessage(toolCall.function.name, {
1492
+ content: "",
1493
+ // v8 ignore next
1494
+ error: error instanceof Error ? error.message : String(error)
1495
+ }));
1475
1496
  }
1476
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools });
1477
- const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
1478
- const newMessages = [...updatedMessages, toolResultMessage];
1479
- setMessages(newMessages);
1480
- await processStream(newMessages, executionMode);
1497
+ nextMessages = [...updatedMessages, ...toolResultMessages];
1498
+ setMessages(nextMessages);
1499
+ break;
1500
+ }
1501
+ if (!nextMessages) {
1502
+ await prewarmCodeBlocks(assistantMessage.content, theme);
1503
+ commitAssistantMessage();
1481
1504
  return;
1482
1505
  }
1506
+ toolTurns += 1;
1507
+ /* v8 ignore start */
1508
+ if (toolTurns >= MAX_TOOL_TURNS) {
1509
+ setMessages([...nextMessages, {
1510
+ role: SYSTEM,
1511
+ content: [
1512
+ "Tool execution stopped because the maximum tool turn limit was reached",
1513
+ ACTION_NOT_PERFORMED,
1514
+ "Summarize completed work and explain what remains without calling more tools."
1515
+ ].join("\n")
1516
+ }]);
1517
+ return;
1518
+ }
1519
+ /* v8 ignore stop */
1520
+ activeMessages = nextMessages;
1483
1521
  }
1484
- await prewarmCodeBlocks(assistantMessage.content, theme);
1485
- commitAssistantMessage();
1486
1522
  } catch (error) {
1487
1523
  // v8 ignore next
1488
1524
  if (!controller.signal.aborted) {
1489
- assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
1490
- await prewarmCodeBlocks(assistantMessage.content, theme);
1491
- commitAssistantMessage();
1525
+ const errorMessage = {
1526
+ role: ASSISTANT,
1527
+ content: `Error: ${error instanceof Error ? error.message : String(error)}`
1528
+ };
1529
+ await prewarmCodeBlocks(errorMessage.content, theme);
1530
+ setStreamingMessage(null);
1531
+ setMessages([...activeMessages, errorMessage]);
1492
1532
  }
1493
1533
  } finally {
1534
+ // v8 ignore next
1494
1535
  if (abortControllerRef.current === controller) abortControllerRef.current = null;
1495
1536
  setIsLoading(false);
1496
1537
  }
@@ -1542,15 +1583,29 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1542
1583
  setStreamingMessage({ ...assistantMessage });
1543
1584
  } else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
1544
1585
  const updatedMessages = commitAssistantMessage();
1545
- if (!READ_TOOLS.has(toolCall.function.name)) {
1546
- const correctionMessage = buildPlanModeCorrectionMessage(toolCall.function.name);
1586
+ let normalized;
1587
+ try {
1588
+ normalized = normalizeToolCall(toolCall);
1589
+ } catch (error) {
1590
+ /* v8 ignore start */
1591
+ const toolResultMessage = buildToolResultMessage(toolCall.function.name, {
1592
+ content: "",
1593
+ error: error instanceof Error ? error.message : String(error)
1594
+ });
1595
+ const newMessages = [...updatedMessages, toolResultMessage];
1596
+ setMessages(newMessages);
1597
+ await processStreamReadOnly(newMessages);
1598
+ return;
1599
+ }
1600
+ if (!READ_TOOLS.has(normalized.name)) {
1601
+ const correctionMessage = buildPlanModeCorrectionMessage(normalized.name);
1547
1602
  const newMessages = [...updatedMessages, correctionMessage];
1548
1603
  setMessages(newMessages);
1549
1604
  await processStreamReadOnly(newMessages);
1550
1605
  return;
1551
1606
  }
1552
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools: READ_TOOLS });
1553
- const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
1607
+ const result = await executeTool(normalized.name, normalized.arguments, { allowedTools: READ_TOOLS });
1608
+ const toolResultMessage = buildToolResultMessage(normalized.name, result);
1554
1609
  const newMessages = [...updatedMessages, toolResultMessage];
1555
1610
  setMessages(newMessages);
1556
1611
  await processStreamReadOnly(newMessages);
@@ -1641,35 +1696,36 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1641
1696
  const handleToolApproval = useCallback(async (decision) => {
1642
1697
  // v8 ignore next
1643
1698
  if (!pendingToolCall) return;
1644
- const toolCall = pendingToolCall;
1699
+ const { executionMode, messages: approvedMessages, toolCall } = pendingToolCall;
1645
1700
  setPendingToolCall(null);
1646
1701
  setIsLoading(true);
1647
1702
  switch (decision) {
1648
1703
  case APPROVE: {
1649
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments);
1704
+ const result = await executeToolCall(toolCall);
1650
1705
  const toolResultMessage = {
1651
1706
  role: SYSTEM,
1652
- content: `Tool ${toolCall.function.name} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1707
+ content: formatToolResultContent(toolCall.function.name, result)
1653
1708
  };
1654
- const newMessages = [...messages, toolResultMessage];
1655
- setMessages((previousMessages) => [...previousMessages, toolResultMessage]);
1656
- await processStream(newMessages);
1709
+ const newMessages = [...approvedMessages, toolResultMessage];
1710
+ setMessages(newMessages);
1711
+ await processStream(newMessages, executionMode);
1657
1712
  break;
1658
1713
  }
1659
- case REJECT:
1660
- setMessages((previousMessages) => [...previousMessages, {
1661
- role: USER,
1662
- content: TURN_ABORTED_MESSAGE
1663
- }]);
1714
+ case REJECT: {
1715
+ const toolResultMessage = {
1716
+ role: SYSTEM,
1717
+ content: formatToolResultContent(toolCall.function.name, {
1718
+ content: "",
1719
+ error: "Tool call rejected by user"
1720
+ })
1721
+ };
1722
+ setMessages([...approvedMessages, toolResultMessage]);
1664
1723
  setIsLoading(false);
1665
1724
  setInterruptReason(InterruptReason.Rejected);
1666
1725
  break;
1726
+ }
1667
1727
  }
1668
- }, [
1669
- pendingToolCall,
1670
- messages,
1671
- processStream
1672
- ]);
1728
+ }, [pendingToolCall, processStream]);
1673
1729
  const handleSubmit = useCallback(async ({ content, images }) => {
1674
1730
  setInterruptReason(null);
1675
1731
  const userContent = content.trim();
@@ -1711,7 +1767,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1711
1767
  theme
1712
1768
  }),
1713
1769
  !pendingPlan && pendingToolCall && /* @__PURE__ */ jsx(ToolApproval, {
1714
- toolCall: pendingToolCall,
1770
+ toolCall: pendingToolCall.toolCall,
1715
1771
  onDecision: handleToolApproval,
1716
1772
  theme
1717
1773
  }),
package/dist/cli.js CHANGED
@@ -8,6 +8,18 @@ import { randomUUID } from "node:crypto";
8
8
  import { Ollama } from "ollama";
9
9
  import { v7 } from "uuid";
10
10
  import { promisify } from "node:util";
11
+ //#region \0rolldown/runtime.js
12
+ var __defProp = Object.defineProperty;
13
+ var __exportAll = (all, no_symbols) => {
14
+ let target = {};
15
+ for (var name in all) __defProp(target, name, {
16
+ get: all[name],
17
+ enumerable: true
18
+ });
19
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
20
+ return target;
21
+ };
22
+ //#endregion
11
23
  //#region src/constants/command.ts
12
24
  var LIST$1 = [
13
25
  {
@@ -38,7 +50,7 @@ var LIST$1 = [
38
50
  //#endregion
39
51
  //#region package.json
40
52
  var name = "code-ollama";
41
- var version = "0.21.0";
53
+ var version = "0.21.1";
42
54
  //#endregion
43
55
  //#region src/constants/package.ts
44
56
  var NAME = name;
@@ -280,6 +292,17 @@ function getTheme(themeId = DEFAULT_THEME_ID) {
280
292
  }
281
293
  //#endregion
282
294
  //#region src/constants/tool.ts
295
+ var tool_exports = /* @__PURE__ */ __exportAll({
296
+ EDIT_FILE: () => EDIT_FILE,
297
+ GREP_SEARCH: () => GREP_SEARCH,
298
+ LIST_DIR: () => LIST_DIR,
299
+ READ_FILE: () => READ_FILE,
300
+ RUN_SHELL: () => RUN_SHELL,
301
+ VIEW_RANGE: () => VIEW_RANGE,
302
+ WEB_FETCH: () => WEB_FETCH,
303
+ WEB_SEARCH: () => WEB_SEARCH,
304
+ WRITE_FILE: () => WRITE_FILE
305
+ });
283
306
  var READ_FILE = "read_file";
284
307
  var WRITE_FILE = "write_file";
285
308
  var EDIT_FILE = "edit_file";
@@ -1170,18 +1193,85 @@ var REQUIRED_STRING_ARGS = {
1170
1193
  [WEB_SEARCH]: ["query"],
1171
1194
  [WEB_FETCH]: ["url"]
1172
1195
  };
1196
+ var TOOL_NAMES = new Set(Object.values(tool_exports));
1197
+ function isToolName(name) {
1198
+ return TOOL_NAMES.has(name);
1199
+ }
1173
1200
  function validateArgs(name, args) {
1174
- const required = REQUIRED_STRING_ARGS[name] ?? [];
1201
+ const required = REQUIRED_STRING_ARGS[name];
1175
1202
  const received = Object.keys(args).join(", ") || "none";
1176
- for (const key of required) if (typeof args[key] !== "string") return {
1203
+ for (const key of required) if (typeof args[key] !== "string" || !args[key]) return {
1177
1204
  content: "",
1178
1205
  error: `Missing required argument: ${key} (received keys: ${received})`
1179
1206
  };
1207
+ if (name === "view_range") {
1208
+ if (!Number.isInteger(args.start) || !Number.isInteger(args.end)) return {
1209
+ content: "",
1210
+ error: `Missing required numeric arguments: start, end (received keys: ${received})`
1211
+ };
1212
+ if (args.start < 1 || args.end < args.start) return {
1213
+ content: "",
1214
+ error: "Invalid line range: start must be >= 1 and end must be >= start"
1215
+ };
1216
+ }
1217
+ if (name === "web_fetch") try {
1218
+ const url = new URL(args.url);
1219
+ if (url.protocol !== "http:" && url.protocol !== "https:") return {
1220
+ content: "",
1221
+ error: "URL must use http or https"
1222
+ };
1223
+ } catch {
1224
+ return {
1225
+ content: "",
1226
+ error: "Invalid URL"
1227
+ };
1228
+ }
1229
+ }
1230
+ function normalizeToolCall(toolCall) {
1231
+ const name = toolCall.function.name;
1232
+ const rawArguments = toolCall.function.arguments;
1233
+ if (!isToolName(name)) throw new Error(`Unknown tool: ${name}`);
1234
+ if (typeof rawArguments !== "object" || rawArguments === null || Array.isArray(rawArguments)) throw new Error(`Invalid arguments for tool: ${name}`);
1235
+ const normalizedArguments = rawArguments;
1236
+ const invalid = validateArgs(name, normalizedArguments);
1237
+ if (invalid?.error) throw new Error(invalid.error);
1238
+ return {
1239
+ name,
1240
+ arguments: normalizedArguments,
1241
+ requiresApproval: WRITE_TOOLS.has(name)
1242
+ };
1243
+ }
1244
+ function formatToolResultContent(toolName, result) {
1245
+ const status = result.error ? "The requested action was NOT performed" : "";
1246
+ const content = result.content ? `\n${result.content}` : "";
1247
+ const error = result.error ? `\nError: ${result.error}` : "";
1248
+ return [
1249
+ `Tool ${toolName} result:`,
1250
+ status,
1251
+ content.trim(),
1252
+ error.trim()
1253
+ ].filter(Boolean).join("\n");
1254
+ }
1255
+ async function executeToolCall(toolCall, options) {
1256
+ try {
1257
+ const normalized = normalizeToolCall(toolCall);
1258
+ return await executeTool(normalized.name, normalized.arguments, options);
1259
+ } catch (error) {
1260
+ return {
1261
+ content: "",
1262
+ // v8 ignore next
1263
+ error: error instanceof Error ? error.message : String(error)
1264
+ };
1265
+ }
1180
1266
  }
1181
1267
  /**
1182
1268
  * Execute a tool by name with arguments
1183
1269
  */
1184
1270
  async function executeTool(name, args, options) {
1271
+ if (!isToolName(name)) return {
1272
+ content: "",
1273
+ error: `Unknown tool: ${name}`
1274
+ };
1185
1275
  if (options?.allowedTools && !options.allowedTools.has(name)) return {
1186
1276
  content: "",
1187
1277
  error: `Tool not allowed: ${name}`
@@ -1199,6 +1289,7 @@ async function executeTool(name, args, options) {
1199
1289
  case VIEW_RANGE: return viewRange(stringArgs.path, args.start, args.end);
1200
1290
  case WEB_SEARCH: return await webSearch(stringArgs.query);
1201
1291
  case WEB_FETCH: return await webFetch(stringArgs.url);
1292
+ // v8 ignore next 2
1202
1293
  default: return {
1203
1294
  content: "",
1204
1295
  error: `Unknown tool: ${name}`
@@ -1240,6 +1331,7 @@ async function checkForUpdate() {
1240
1331
  //#endregion
1241
1332
  //#region src/cli.ts
1242
1333
  var cli = cac("code-ollama");
1334
+ var MAX_TOOL_TURNS = 25;
1243
1335
  cli.version(VERSION);
1244
1336
  cli.help();
1245
1337
  cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model, prompt) => {
@@ -1272,30 +1364,40 @@ async function runPrompt(model, prompt) {
1272
1364
  write("\n");
1273
1365
  }
1274
1366
  async function processRunStream(messages, model) {
1275
- const assistantMessage = {
1276
- role: ASSISTANT,
1277
- content: ""
1278
- };
1279
- for await (const chunk of streamChat(messages, model, TOOLS)) {
1280
- if (chunk.type === "content") {
1281
- assistantMessage.content += chunk.content;
1282
- write(chunk.content);
1283
- continue;
1284
- }
1285
- for (const toolCall of chunk.tool_calls) {
1286
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments);
1287
- const toolResultMessage = {
1288
- role: SYSTEM,
1289
- content: `Tool ${toolCall.function.name} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1290
- };
1291
- await processRunStream([
1292
- ...messages,
1293
- assistantMessage,
1294
- toolResultMessage
1295
- ], model);
1296
- return;
1367
+ let activeMessages = messages;
1368
+ let toolTurns = 0;
1369
+ while (toolTurns < MAX_TOOL_TURNS) {
1370
+ const assistantMessage = {
1371
+ role: ASSISTANT,
1372
+ content: ""
1373
+ };
1374
+ let nextMessages = null;
1375
+ for await (const chunk of streamChat(activeMessages, model, TOOLS)) {
1376
+ if (chunk.type === "content") {
1377
+ assistantMessage.content += chunk.content;
1378
+ write(chunk.content);
1379
+ continue;
1380
+ }
1381
+ // v8 ignore next 3
1382
+ if (chunk.tool_calls.length === 0) continue;
1383
+ const committedMessages = [...activeMessages, assistantMessage];
1384
+ const toolResultMessages = [];
1385
+ for (const toolCall of chunk.tool_calls) {
1386
+ const result = await executeToolCall(toolCall);
1387
+ toolResultMessages.push({
1388
+ role: SYSTEM,
1389
+ content: formatToolResultContent(toolCall.function.name, result)
1390
+ });
1391
+ }
1392
+ nextMessages = [...committedMessages, ...toolResultMessages];
1393
+ break;
1297
1394
  }
1395
+ if (!nextMessages) return;
1396
+ activeMessages = nextMessages;
1397
+ toolTurns += 1;
1298
1398
  }
1399
+ // v8 ignore next 3
1400
+ writeError("Tool execution stopped because the maximum tool turn limit was reached\n");
1299
1401
  }
1300
1402
  async function main(args = process.argv.slice(2)) {
1301
1403
  if (args.length) cli.parse([
@@ -1306,7 +1408,7 @@ async function main(args = process.argv.slice(2)) {
1306
1408
  else await launchTui();
1307
1409
  }
1308
1410
  async function launchTui(sessionId) {
1309
- const { renderApp } = await import("./assets/tui-48fT4HLs.js");
1411
+ const { renderApp } = await import("./assets/tui-BHkHnKUC.js");
1310
1412
  reset();
1311
1413
  renderApp(sessionId);
1312
1414
  }
@@ -1322,4 +1424,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
1322
1424
  if (isEntrypoint()) main();
1323
1425
  // v8 ignore stop
1324
1426
  //#endregion
1325
- export { HEADER_PREFIX as A, AUTO as B, streamChat as C, saveClipboardImage as D, removeClipboardImage as E, SYSTEM as F, REJECT as G, PLAN as H, USER as I, LIST$1 as J, NAME as K, PLAN_GENERATION_INSTRUCTION as L, LIST as M, getTheme as N, resetSystemMessage as O, ASSISTANT as P, BACK as R, pullModel as S, saveConfig as T, SAFE as U, LABEL as V, APPROVE as W, reset as _, WRITE_TOOLS as a, deleteModel as b, write as c, deleteSession as d, deleteSessionIfEmpty as f, clear as g, updateSessionModel as h, TOOLS as i, WARNING as j, withSystemMessage as k, appendMessage as l, loadSession as m, main, executeTool as n, tick as o, listSessions as p, VERSION as q, READ_TOOLS as r, color as s, checkForUpdate as t, createSession as u, setClearHandler as v, loadConfig as w, listModels as x, checkHealth as y, CATALOG as z };
1427
+ export { saveClipboardImage as A, PLAN_GENERATION_INSTRUCTION as B, deleteModel as C, loadConfig as D, streamChat as E, LIST as F, PLAN as G, CATALOG as H, getTheme as I, REJECT as J, SAFE as K, ASSISTANT as L, withSystemMessage as M, HEADER_PREFIX as N, saveConfig as O, WARNING as P, SYSTEM as R, checkHealth as S, pullModel as T, AUTO as U, BACK as V, LABEL as W, VERSION as X, NAME as Y, LIST$1 as Z, loadSession as _, normalizeToolCall as a, reset as b, WRITE_TOOLS as c, write as d, appendMessage as f, listSessions as g, deleteSessionIfEmpty as h, formatToolResultContent as i, resetSystemMessage as j, removeClipboardImage as k, tick as l, deleteSession as m, main, executeTool as n, READ_TOOLS as o, createSession as p, APPROVE as q, executeToolCall as r, TOOLS as s, checkForUpdate as t, color as u, updateSessionModel as v, listModels as w, setClearHandler as x, clear as y, USER as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "description": "Ollama coding agent that runs in your terminal",
5
5
  "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
6
  "type": "module",
@@ -42,34 +42,34 @@
42
42
  "@inkjs/ui": "2.0.0",
43
43
  "@shikijs/cli": "4.1.0",
44
44
  "cac": "7.0.0",
45
- "ink": "7.0.3",
45
+ "ink": "7.0.5",
46
46
  "marked": "15.0.12",
47
47
  "marked-terminal": "7.3.0",
48
48
  "ollama": "0.6.3",
49
- "react": "19.2.6",
49
+ "react": "19.2.7",
50
50
  "uuid": "14.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@commitlint/cli": "21.0.1",
54
- "@commitlint/config-conventional": "21.0.1",
53
+ "@commitlint/cli": "21.0.2",
54
+ "@commitlint/config-conventional": "21.0.2",
55
55
  "@eslint/js": "10.0.1",
56
56
  "@types/node": "25.9.1",
57
- "@types/react": "19.2.15",
58
- "@vitest/coverage-v8": "4.1.7",
59
- "eslint": "10.4.0",
60
- "eslint-plugin-prettier": "5.5.5",
57
+ "@types/react": "19.2.16",
58
+ "@vitest/coverage-v8": "4.1.8",
59
+ "eslint": "10.4.1",
60
+ "eslint-plugin-prettier": "5.5.6",
61
61
  "eslint-plugin-simple-import-sort": "13.0.0",
62
62
  "globals": "17.6.0",
63
63
  "husky": "9.1.7",
64
64
  "ink-testing-library": "4.0.0",
65
- "lint-staged": "17.0.5",
65
+ "lint-staged": "17.0.7",
66
66
  "prettier": "3.8.3",
67
67
  "publint": "0.3.21",
68
- "tsx": "4.22.3",
68
+ "tsx": "4.22.4",
69
69
  "typescript": "6.0.3",
70
- "typescript-eslint": "8.59.4",
71
- "vite": "8.0.14",
72
- "vitest": "4.1.7"
70
+ "typescript-eslint": "8.60.1",
71
+ "vite": "8.0.16",
72
+ "vitest": "4.1.8"
73
73
  },
74
74
  "files": [
75
75
  "dist/"