@polka-codes/core 0.8.10 → 0.8.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,8 +7,10 @@ var __export = (target, all) => {
7
7
  // src/AiService/AiServiceBase.ts
8
8
  var AiServiceBase = class {
9
9
  usageMeter;
10
- constructor(usageMeter) {
11
- this.usageMeter = usageMeter;
10
+ options;
11
+ constructor(options) {
12
+ this.options = options;
13
+ this.usageMeter = options.usageMeter;
12
14
  }
13
15
  async *send(systemPrompt, messages) {
14
16
  this.usageMeter.checkLimit();
@@ -169,7 +171,7 @@ var AnthropicService = class extends AiServiceBase {
169
171
  #client;
170
172
  model;
171
173
  constructor(options) {
172
- super(options.usageMeter);
174
+ super(options);
173
175
  this.#options = options;
174
176
  this.#client = new Anthropic({
175
177
  apiKey: options.apiKey,
@@ -448,7 +450,7 @@ var DeepSeekService = class extends AiServiceBase {
448
450
  #client;
449
451
  model;
450
452
  constructor(options) {
451
- super(options.usageMeter);
453
+ super(options);
452
454
  this.#client = new OpenAI({
453
455
  baseURL: "https://api.deepseek.com/v1",
454
456
  apiKey: options.apiKey
@@ -511,7 +513,7 @@ var OllamaService = class extends AiServiceBase {
511
513
  #client;
512
514
  model;
513
515
  constructor(options) {
514
- super(options.usageMeter);
516
+ super(options);
515
517
  this.#client = new OpenAI2({
516
518
  baseURL: `${options.baseUrl || "http://localhost:11434"}/v1`,
517
519
  apiKey: "ollama"
@@ -554,7 +556,7 @@ var OpenRouterService = class extends AiServiceBase {
554
556
  #modelProviderInfo;
555
557
  model;
556
558
  constructor(options) {
557
- super(options.usageMeter);
559
+ super(options);
558
560
  if (!options.model) {
559
561
  throw new Error("OpenRouter requires a model");
560
562
  }
@@ -876,9 +878,6 @@ var MockProvider = class {
876
878
  async searchFiles(path, regex, filePattern) {
877
879
  return ["mock-file.txt"];
878
880
  }
879
- async listCodeDefinitionNames(path) {
880
- return "mockDefinition";
881
- }
882
881
  async executeCommand(command, needApprove) {
883
882
  return { stdout: "mock output", stderr: "", exitCode: 0 };
884
883
  }
@@ -896,14 +895,13 @@ __export(allTools_exports, {
896
895
  askFollowupQuestion: () => askFollowupQuestion_default,
897
896
  attemptCompletion: () => attemptCompletion_default,
898
897
  delegate: () => delegate_default,
898
+ editFile: () => editFile_default,
899
899
  executeCommand: () => executeCommand_default,
900
900
  handOver: () => handOver_default,
901
- listCodeDefinitionNames: () => listCodeDefinitionNames_default,
902
901
  listFiles: () => listFiles_default,
903
902
  readFile: () => readFile_default,
904
903
  removeFile: () => removeFile_default,
905
904
  renameFile: () => renameFile_default,
906
- replaceInFile: () => replaceInFile_default,
907
905
  searchFiles: () => searchFiles_default,
908
906
  updateKnowledge: () => updateKnowledge_default,
909
907
  writeToFile: () => writeToFile_default
@@ -929,61 +927,85 @@ var ToolResponseType = /* @__PURE__ */ ((ToolResponseType2) => {
929
927
  return ToolResponseType2;
930
928
  })(ToolResponseType || {});
931
929
 
932
- // src/tools/utils/replaceInFile.ts
933
- var replaceInFile = async (fileContent, diff) => {
934
- const blockPattern = /<<<<<+ SEARCH\s*\r?\n([\s\S]*?)\r?\n=======[ \t]*\r?\n([\s\S]*?)\r?\n?>>>>>+ REPLACE/g;
935
- const blocks = [];
936
- for (let match = blockPattern.exec(diff); match !== null; match = blockPattern.exec(diff)) {
937
- blocks.push({ search: match[1], replace: match[2] });
930
+ // src/tools/utils/editFile.ts
931
+ var START_OF_FILE = "<<<START_OF_FILE>>>";
932
+ var END_OF_FILE = "<<<END_OF_FILE>>>";
933
+ var editFile = async (fileContent, operations) => {
934
+ if (!operations || operations.length === 0) {
935
+ throw new Error("At least one edit operation is required");
938
936
  }
939
- if (blocks.length === 0) {
940
- throw new Error("No valid diff blocks found.");
937
+ const originalLines = fileContent.split("\n");
938
+ let updatedContent = fileContent;
939
+ for (const operation of operations) {
940
+ updatedContent = await applyEditOperation(updatedContent, operation, originalLines);
941
941
  }
942
- const findAndReplace = (content, search, replace) => {
943
- let index = content.indexOf(search);
944
- if (index !== -1) {
945
- return content.slice(0, index) + replace + content.slice(index + search.length);
942
+ return updatedContent;
943
+ };
944
+ async function applyEditOperation(fileContent, operation, originalLines) {
945
+ const { before_text, after_text, new_text, before_text_line_start, after_text_line_start } = operation;
946
+ if (before_text === START_OF_FILE && after_text === END_OF_FILE) {
947
+ return new_text;
948
+ }
949
+ if (before_text === START_OF_FILE) {
950
+ if (!after_text) {
951
+ return new_text + fileContent;
946
952
  }
947
- const trimmedSearch = search.trim();
948
- const trimmedContent = content.trim();
949
- const offset = content.indexOf(trimmedContent);
950
- index = trimmedContent.indexOf(trimmedSearch);
951
- if (index !== -1) {
952
- const absoluteIndex = offset + index;
953
- return content.slice(0, absoluteIndex) + replace + content.slice(absoluteIndex + trimmedSearch.length);
953
+ const afterIndex = findTextWithHint(fileContent, after_text, after_text_line_start, originalLines);
954
+ return new_text + fileContent.slice(afterIndex);
955
+ }
956
+ if (after_text === END_OF_FILE) {
957
+ if (!before_text) {
958
+ return fileContent + new_text;
954
959
  }
955
- const normalizedSearch = trimmedSearch.replace(/\s+/g, " ");
956
- const normalizedContent = trimmedContent.replace(/\s+/g, " ");
957
- index = normalizedContent.indexOf(normalizedSearch);
958
- if (index !== -1) {
959
- let runningIndex = 0;
960
- let actualPos = offset;
961
- for (const segment of trimmedSearch.replace(/\s+/g, " ").split(" ")) {
962
- const segIndex = content.indexOf(segment, actualPos);
963
- if (segIndex === -1) {
964
- break;
965
- }
966
- if (runningIndex === 0) {
967
- actualPos = segIndex;
968
- } else {
969
- actualPos = segIndex + segment.length;
970
- }
971
- runningIndex++;
960
+ const beforeIndex = findTextWithHint(fileContent, before_text, before_text_line_start, originalLines);
961
+ const beforeEndIndex = beforeIndex + before_text.length;
962
+ return fileContent.slice(0, beforeEndIndex) + new_text;
963
+ }
964
+ if (before_text && after_text) {
965
+ const beforeIndex = findTextWithHint(fileContent, before_text, before_text_line_start, originalLines);
966
+ const beforeEndIndex = beforeIndex + before_text.length;
967
+ const afterIndex = findTextWithHint(fileContent, after_text, after_text_line_start, originalLines, beforeEndIndex);
968
+ return fileContent.slice(0, beforeEndIndex) + new_text + fileContent.slice(afterIndex);
969
+ }
970
+ if (before_text) {
971
+ const beforeIndex = findTextWithHint(fileContent, before_text, before_text_line_start, originalLines);
972
+ const beforeEndIndex = beforeIndex + before_text.length;
973
+ return fileContent.slice(0, beforeEndIndex) + new_text + fileContent.slice(beforeEndIndex);
974
+ }
975
+ if (after_text) {
976
+ const afterIndex = findTextWithHint(fileContent, after_text, after_text_line_start, originalLines);
977
+ return fileContent.slice(0, afterIndex) + new_text + fileContent.slice(afterIndex);
978
+ }
979
+ throw new Error("Either before_text or after_text must be specified");
980
+ }
981
+ function findTextWithHint(content, searchText, lineHint, originalLines, startIndex = 0) {
982
+ if (lineHint && lineHint > 0 && lineHint <= originalLines.length) {
983
+ const hintIndex = getLineStartIndex(originalLines, lineHint - 1);
984
+ const searchRadius = 5;
985
+ const windowStart = Math.max(0, hintIndex - searchRadius * 50);
986
+ const windowEnd = Math.min(content.length, hintIndex + searchRadius * 50);
987
+ const windowContent = content.slice(windowStart, windowEnd);
988
+ const relativeIndex = windowContent.indexOf(searchText);
989
+ if (relativeIndex !== -1) {
990
+ const absoluteIndex = windowStart + relativeIndex;
991
+ if (absoluteIndex >= startIndex) {
992
+ return absoluteIndex;
972
993
  }
973
- const strippedSearch = trimmedSearch.replace(/\s+/g, "");
974
- const endPos = actualPos;
975
- const startPos = endPos - strippedSearch.length;
976
- return content.slice(0, startPos) + replace + content.slice(endPos);
977
994
  }
978
- throw new Error(`Could not find the following text in file:
979
- ${search}`);
980
- };
981
- let updatedFile = fileContent;
982
- for (const { search, replace } of blocks) {
983
- updatedFile = findAndReplace(updatedFile, search, replace);
984
995
  }
985
- return updatedFile;
986
- };
996
+ const index = content.indexOf(searchText, startIndex);
997
+ if (index === -1) {
998
+ throw new Error(`Could not find text: ${searchText}`);
999
+ }
1000
+ return index;
1001
+ }
1002
+ function getLineStartIndex(lines, lineIndex) {
1003
+ let index = 0;
1004
+ for (let i = 0; i < lineIndex && i < lines.length; i++) {
1005
+ index += lines[i].length + 1;
1006
+ }
1007
+ return index;
1008
+ }
987
1009
 
988
1010
  // src/tools/utils/getArg.ts
989
1011
  var getString = (args, name, defaultValue) => {
@@ -1077,6 +1099,62 @@ var getArray = (args, name, defaultValue) => {
1077
1099
  return [ret];
1078
1100
  };
1079
1101
 
1102
+ // src/tools/utils/replaceInFile.ts
1103
+ var replaceInFile = async (fileContent, diff) => {
1104
+ const blockPattern = /<<<<<+ SEARCH\s*\r?\n([\s\S]*?)\r?\n=======[ \t]*\r?\n([\s\S]*?)\r?\n?>>>>>+ REPLACE/g;
1105
+ const blocks = [];
1106
+ for (let match = blockPattern.exec(diff); match !== null; match = blockPattern.exec(diff)) {
1107
+ blocks.push({ search: match[1], replace: match[2] });
1108
+ }
1109
+ if (blocks.length === 0) {
1110
+ throw new Error("No valid diff blocks found.");
1111
+ }
1112
+ const findAndReplace = (content, search, replace) => {
1113
+ let index = content.indexOf(search);
1114
+ if (index !== -1) {
1115
+ return content.slice(0, index) + replace + content.slice(index + search.length);
1116
+ }
1117
+ const trimmedSearch = search.trim();
1118
+ const trimmedContent = content.trim();
1119
+ const offset = content.indexOf(trimmedContent);
1120
+ index = trimmedContent.indexOf(trimmedSearch);
1121
+ if (index !== -1) {
1122
+ const absoluteIndex = offset + index;
1123
+ return content.slice(0, absoluteIndex) + replace + content.slice(absoluteIndex + trimmedSearch.length);
1124
+ }
1125
+ const normalizedSearch = trimmedSearch.replace(/\s+/g, " ");
1126
+ const normalizedContent = trimmedContent.replace(/\s+/g, " ");
1127
+ index = normalizedContent.indexOf(normalizedSearch);
1128
+ if (index !== -1) {
1129
+ let runningIndex = 0;
1130
+ let actualPos = offset;
1131
+ for (const segment of trimmedSearch.replace(/\s+/g, " ").split(" ")) {
1132
+ const segIndex = content.indexOf(segment, actualPos);
1133
+ if (segIndex === -1) {
1134
+ break;
1135
+ }
1136
+ if (runningIndex === 0) {
1137
+ actualPos = segIndex;
1138
+ } else {
1139
+ actualPos = segIndex + segment.length;
1140
+ }
1141
+ runningIndex++;
1142
+ }
1143
+ const strippedSearch = trimmedSearch.replace(/\s+/g, "");
1144
+ const endPos = actualPos;
1145
+ const startPos = endPos - strippedSearch.length;
1146
+ return content.slice(0, startPos) + replace + content.slice(endPos);
1147
+ }
1148
+ throw new Error(`Could not find the following text in file:
1149
+ ${search}`);
1150
+ };
1151
+ let updatedFile = fileContent;
1152
+ for (const { search, replace } of blocks) {
1153
+ updatedFile = findAndReplace(updatedFile, search, replace);
1154
+ }
1155
+ return updatedFile;
1156
+ };
1157
+
1080
1158
  // src/tools/askFollowupQuestion.ts
1081
1159
  var toolInfo = {
1082
1160
  name: "ask_followup_question",
@@ -1387,59 +1465,8 @@ var executeCommand_default = {
1387
1465
  isAvailable: isAvailable4
1388
1466
  };
1389
1467
 
1390
- // src/tools/listCodeDefinitionNames.ts
1391
- var toolInfo5 = {
1392
- name: "list_code_definition_names",
1393
- description: "Request to list definition names (classes, functions, methods, etc.) used for all files in a directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.",
1394
- parameters: [
1395
- {
1396
- name: "path",
1397
- description: "The path of a code file to list top level source code definitions for.",
1398
- required: true,
1399
- usageValue: "Directory path here"
1400
- }
1401
- ],
1402
- examples: [
1403
- {
1404
- description: "Request to list code definition names in a directory",
1405
- parameters: [
1406
- {
1407
- name: "path",
1408
- value: "src/utils"
1409
- }
1410
- ]
1411
- }
1412
- ],
1413
- permissionLevel: 1 /* Read */
1414
- };
1415
- var handler5 = async (provider, args) => {
1416
- if (!provider.listCodeDefinitionNames) {
1417
- return {
1418
- type: "Error" /* Error */,
1419
- message: "Not possible to list code definition names. Abort."
1420
- };
1421
- }
1422
- const path = getString(args, "path");
1423
- const result = await provider.listCodeDefinitionNames(path);
1424
- return {
1425
- type: "Reply" /* Reply */,
1426
- message: `<list_code_definition_names_path>${path}</list_code_definition_names_path>
1427
- <list_code_definition_names_result>
1428
- ${result}
1429
- </list_code_definition_names_result>`
1430
- };
1431
- };
1432
- var isAvailable5 = (provider) => {
1433
- return !!provider.listCodeDefinitionNames;
1434
- };
1435
- var listCodeDefinitionNames_default = {
1436
- ...toolInfo5,
1437
- handler: handler5,
1438
- isAvailable: isAvailable5
1439
- };
1440
-
1441
1468
  // src/tools/listFiles.ts
1442
- var toolInfo6 = {
1469
+ var toolInfo5 = {
1443
1470
  name: "list_files",
1444
1471
  description: "Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.",
1445
1472
  parameters: [
@@ -1479,7 +1506,7 @@ var toolInfo6 = {
1479
1506
  ],
1480
1507
  permissionLevel: 1 /* Read */
1481
1508
  };
1482
- var handler6 = async (provider, args) => {
1509
+ var handler5 = async (provider, args) => {
1483
1510
  if (!provider.listFiles) {
1484
1511
  return {
1485
1512
  type: "Error" /* Error */,
@@ -1499,17 +1526,17 @@ ${files.join("\n")}
1499
1526
  <list_files_truncated>${limitReached}</list_files_truncated>`
1500
1527
  };
1501
1528
  };
1502
- var isAvailable6 = (provider) => {
1529
+ var isAvailable5 = (provider) => {
1503
1530
  return !!provider.listFiles;
1504
1531
  };
1505
1532
  var listFiles_default = {
1506
- ...toolInfo6,
1507
- handler: handler6,
1508
- isAvailable: isAvailable6
1533
+ ...toolInfo5,
1534
+ handler: handler5,
1535
+ isAvailable: isAvailable5
1509
1536
  };
1510
1537
 
1511
1538
  // src/tools/readFile.ts
1512
- var toolInfo7 = {
1539
+ var toolInfo6 = {
1513
1540
  name: "read_file",
1514
1541
  description: "Request to read the contents of one or multiple files at the specified paths. Use comma separated paths to read multiple files. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. May not be suitable for other types of binary files, as it returns the raw content as a string. Try to list all the potential files are relevent to the task, and then use this tool to read all the relevant files.",
1515
1542
  parameters: [
@@ -1542,7 +1569,7 @@ var toolInfo7 = {
1542
1569
  ],
1543
1570
  permissionLevel: 1 /* Read */
1544
1571
  };
1545
- var handler7 = async (provider, args) => {
1572
+ var handler6 = async (provider, args) => {
1546
1573
  if (!provider.readFile) {
1547
1574
  return {
1548
1575
  type: "Error" /* Error */,
@@ -1569,135 +1596,17 @@ var handler7 = async (provider, args) => {
1569
1596
  message: resp.join("\n")
1570
1597
  };
1571
1598
  };
1572
- var isAvailable7 = (provider) => {
1599
+ var isAvailable6 = (provider) => {
1573
1600
  return !!provider.readFile;
1574
1601
  };
1575
1602
  var readFile_default = {
1576
- ...toolInfo7,
1577
- handler: handler7,
1578
- isAvailable: isAvailable7
1579
- };
1580
-
1581
- // src/tools/replaceInFile.ts
1582
- var toolInfo8 = {
1583
- name: "replace_in_file",
1584
- description: "Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.",
1585
- parameters: [
1586
- {
1587
- name: "path",
1588
- description: "The path of the file to modify",
1589
- required: true,
1590
- usageValue: "File path here"
1591
- },
1592
- {
1593
- name: "diff",
1594
- description: `One or more SEARCH/REPLACE blocks following this exact format:
1595
- \`\`\`
1596
- <<<<<<< SEARCH
1597
- [exact content to find]
1598
- =======
1599
- [new content to replace with]
1600
- >>>>>>> REPLACE
1601
- \`\`\`
1602
- Critical rules:
1603
- 1. SEARCH content must match the associated file section to find EXACTLY:
1604
- * Match character-for-character including whitespace, indentation, line endings
1605
- * Include all comments, docstrings, etc.
1606
- 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.
1607
- * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.
1608
- * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.
1609
- * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.
1610
- 3. Keep SEARCH/REPLACE blocks concise:
1611
- * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.
1612
- * Include just the changing lines, and a few surrounding lines if needed for uniqueness.
1613
- * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.
1614
- * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.
1615
- 4. Special operations:
1616
- * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
1617
- * To delete code: Use empty REPLACE section`,
1618
- required: true,
1619
- usageValue: "Search and replace blocks here"
1620
- }
1621
- ],
1622
- examples: [
1623
- {
1624
- description: "Request to replace sections of content in a file",
1625
- parameters: [
1626
- {
1627
- name: "path",
1628
- value: "src/main.js"
1629
- },
1630
- {
1631
- name: "diff",
1632
- value: `
1633
- <<<<<<< SEARCH
1634
- import React from 'react';
1635
- =======
1636
- import React, { useState } from 'react';
1637
- >>>>>>> REPLACE
1638
-
1639
- <<<<<<< SEARCH
1640
- function handleSubmit() {
1641
- saveData();
1642
- setLoading(false);
1643
- }
1644
-
1645
- =======
1646
- >>>>>>> REPLACE
1647
-
1648
- <<<<<<< SEARCH
1649
- return (
1650
- <div>
1651
- =======
1652
- function handleSubmit() {
1653
- saveData();
1654
- setLoading(false);
1655
- }
1656
-
1657
- return (
1658
- <div>
1659
- >>>>>>> REPLACE
1660
- `
1661
- }
1662
- ]
1663
- }
1664
- ],
1665
- permissionLevel: 2 /* Write */
1666
- };
1667
- var handler8 = async (provider, args) => {
1668
- if (!provider.readFile || !provider.writeFile) {
1669
- return {
1670
- type: "Error" /* Error */,
1671
- message: "Not possible to replace in file. Abort."
1672
- };
1673
- }
1674
- const path = getString(args, "path");
1675
- const diff = getString(args, "diff");
1676
- const fileContent = await provider.readFile(path);
1677
- if (fileContent == null) {
1678
- return {
1679
- type: "Error" /* Error */,
1680
- message: `<error><replace_in_file_path>${path}</replace_in_file_path><error_message>File not found</error_message></error>`
1681
- };
1682
- }
1683
- const result = await replaceInFile(fileContent, diff);
1684
- await provider.writeFile(path, result);
1685
- return {
1686
- type: "Reply" /* Reply */,
1687
- message: `<replace_in_file_path>${path}</replace_in_file_path>`
1688
- };
1689
- };
1690
- var isAvailable8 = (provider) => {
1691
- return !!provider.readFile && !!provider.writeFile;
1692
- };
1693
- var replaceInFile_default = {
1694
- ...toolInfo8,
1695
- handler: handler8,
1696
- isAvailable: isAvailable8
1603
+ ...toolInfo6,
1604
+ handler: handler6,
1605
+ isAvailable: isAvailable6
1697
1606
  };
1698
1607
 
1699
1608
  // src/tools/searchFiles.ts
1700
- var toolInfo9 = {
1609
+ var toolInfo7 = {
1701
1610
  name: "search_files",
1702
1611
  description: "Request to perform a regex search across files in a specified directory, outputting context-rich results that include surrounding lines. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.",
1703
1612
  parameters: [
@@ -1741,7 +1650,7 @@ var toolInfo9 = {
1741
1650
  ],
1742
1651
  permissionLevel: 1 /* Read */
1743
1652
  };
1744
- var handler9 = async (provider, args) => {
1653
+ var handler7 = async (provider, args) => {
1745
1654
  if (!provider.searchFiles) {
1746
1655
  return {
1747
1656
  type: "Error" /* Error */,
@@ -1763,19 +1672,19 @@ ${files.join("\n")}
1763
1672
  `
1764
1673
  };
1765
1674
  };
1766
- var isAvailable9 = (provider) => {
1675
+ var isAvailable7 = (provider) => {
1767
1676
  return !!provider.searchFiles;
1768
1677
  };
1769
1678
  var searchFiles_default = {
1770
- ...toolInfo9,
1771
- handler: handler9,
1772
- isAvailable: isAvailable9
1679
+ ...toolInfo7,
1680
+ handler: handler7,
1681
+ isAvailable: isAvailable7
1773
1682
  };
1774
1683
 
1775
1684
  // src/tools/updateKnowledge.ts
1776
1685
  import { join } from "node:path";
1777
1686
  import YAML from "yaml";
1778
- var toolInfo10 = {
1687
+ var toolInfo8 = {
1779
1688
  name: "update_knowledge",
1780
1689
  description: "Update knowledge in a knowledge.ai.yml file with smart merging capabilities. This tool lets you add, update, or remove information using path-based updates and special directives.",
1781
1690
  parameters: [
@@ -1949,7 +1858,7 @@ function deepMerge(target, source) {
1949
1858
  }
1950
1859
  return output;
1951
1860
  }
1952
- var handler10 = async (provider, args) => {
1861
+ var handler8 = async (provider, args) => {
1953
1862
  if (!provider.readFile || !provider.writeFile) {
1954
1863
  return {
1955
1864
  type: "Error" /* Error */,
@@ -2021,17 +1930,17 @@ var handler10 = async (provider, args) => {
2021
1930
  };
2022
1931
  }
2023
1932
  };
2024
- var isAvailable10 = (provider) => {
1933
+ var isAvailable8 = (provider) => {
2025
1934
  return !!provider.readFile && !!provider.writeFile;
2026
1935
  };
2027
1936
  var updateKnowledge_default = {
2028
- ...toolInfo10,
2029
- handler: handler10,
2030
- isAvailable: isAvailable10
1937
+ ...toolInfo8,
1938
+ handler: handler8,
1939
+ isAvailable: isAvailable8
2031
1940
  };
2032
1941
 
2033
1942
  // src/tools/writeToFile.ts
2034
- var toolInfo11 = {
1943
+ var toolInfo9 = {
2035
1944
  name: "write_to_file",
2036
1945
  description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;` and `&gt;`.",
2037
1946
  parameters: [
@@ -2076,7 +1985,7 @@ export default App;
2076
1985
  ],
2077
1986
  permissionLevel: 2 /* Write */
2078
1987
  };
2079
- var handler11 = async (provider, args) => {
1988
+ var handler9 = async (provider, args) => {
2080
1989
  if (!provider.writeFile) {
2081
1990
  return {
2082
1991
  type: "Error" /* Error */,
@@ -2091,17 +2000,17 @@ var handler11 = async (provider, args) => {
2091
2000
  message: `<write_to_file_path>${path}</write_to_file_path><status>Success</status>`
2092
2001
  };
2093
2002
  };
2094
- var isAvailable11 = (provider) => {
2003
+ var isAvailable9 = (provider) => {
2095
2004
  return !!provider.writeFile;
2096
2005
  };
2097
2006
  var writeToFile_default = {
2098
- ...toolInfo11,
2099
- handler: handler11,
2100
- isAvailable: isAvailable11
2007
+ ...toolInfo9,
2008
+ handler: handler9,
2009
+ isAvailable: isAvailable9
2101
2010
  };
2102
2011
 
2103
2012
  // src/tools/handOver.ts
2104
- var toolInfo12 = {
2013
+ var toolInfo10 = {
2105
2014
  name: "hand_over",
2106
2015
  description: "Hand over the current task to another agent to complete. This tool MUST NOT to be used with any other tool.",
2107
2016
  parameters: [
@@ -2124,79 +2033,274 @@ var toolInfo12 = {
2124
2033
  usageValue: "Context information"
2125
2034
  },
2126
2035
  {
2127
- name: "files",
2128
- description: "The files relevant to the task. Comma separated paths",
2129
- required: false,
2130
- usageValue: "Relevant files"
2036
+ name: "files",
2037
+ description: "The files relevant to the task. Comma separated paths",
2038
+ required: false,
2039
+ usageValue: "Relevant files"
2040
+ }
2041
+ ],
2042
+ examples: [
2043
+ {
2044
+ description: "Hand over a coding task to the coder agent",
2045
+ parameters: [
2046
+ {
2047
+ name: "agent_name",
2048
+ value: "coder"
2049
+ },
2050
+ {
2051
+ name: "task",
2052
+ value: "Implement the login feature"
2053
+ },
2054
+ {
2055
+ name: "context",
2056
+ value: "We need a secure login system with email and password"
2057
+ },
2058
+ {
2059
+ name: "files",
2060
+ value: "src/auth/login.ts,src/auth/types.ts"
2061
+ }
2062
+ ]
2063
+ }
2064
+ ],
2065
+ permissionLevel: 0 /* None */
2066
+ };
2067
+ var handler10 = async (_provider, args) => {
2068
+ const agentName = getString(args, "agent_name");
2069
+ const task = getString(args, "task");
2070
+ const context = getString(args, "context", void 0);
2071
+ const files = getStringArray(args, "files", []);
2072
+ return {
2073
+ type: "HandOver" /* HandOver */,
2074
+ agentName,
2075
+ task,
2076
+ context,
2077
+ files
2078
+ // originalTask will be set by AgentBase
2079
+ };
2080
+ };
2081
+ var isAvailable10 = (_provider) => {
2082
+ return true;
2083
+ };
2084
+ var handOver_default = {
2085
+ ...toolInfo10,
2086
+ handler: handler10,
2087
+ isAvailable: isAvailable10
2088
+ };
2089
+
2090
+ // src/tools/removeFile.ts
2091
+ var toolInfo11 = {
2092
+ name: "remove_file",
2093
+ description: "Request to remove a file at the specified path.",
2094
+ parameters: [
2095
+ {
2096
+ name: "path",
2097
+ description: "The path of the file to remove",
2098
+ required: true,
2099
+ usageValue: "File path here"
2100
+ }
2101
+ ],
2102
+ examples: [
2103
+ {
2104
+ description: "Request to remove a file",
2105
+ parameters: [
2106
+ {
2107
+ name: "path",
2108
+ value: "src/main.js"
2109
+ }
2110
+ ]
2111
+ }
2112
+ ],
2113
+ permissionLevel: 2 /* Write */
2114
+ };
2115
+ var handler11 = async (provider, args) => {
2116
+ if (!provider.removeFile) {
2117
+ return {
2118
+ type: "Error" /* Error */,
2119
+ message: "Not possible to remove file. Abort."
2120
+ };
2121
+ }
2122
+ const path = getString(args, "path");
2123
+ await provider.removeFile(path);
2124
+ return {
2125
+ type: "Reply" /* Reply */,
2126
+ message: `<remove_file_path>${path}</remove_file_path><status>Success</status>`
2127
+ };
2128
+ };
2129
+ var isAvailable11 = (provider) => {
2130
+ return !!provider.removeFile;
2131
+ };
2132
+ var removeFile_default = {
2133
+ ...toolInfo11,
2134
+ handler: handler11,
2135
+ isAvailable: isAvailable11
2136
+ };
2137
+
2138
+ // src/tools/renameFile.ts
2139
+ var toolInfo12 = {
2140
+ name: "rename_file",
2141
+ description: "Request to rename a file from source path to target path.",
2142
+ parameters: [
2143
+ {
2144
+ name: "source_path",
2145
+ description: "The current path of the file",
2146
+ required: true,
2147
+ usageValue: "Source file path here"
2148
+ },
2149
+ {
2150
+ name: "target_path",
2151
+ description: "The new path for the file",
2152
+ required: true,
2153
+ usageValue: "Target file path here"
2131
2154
  }
2132
2155
  ],
2133
2156
  examples: [
2134
2157
  {
2135
- description: "Hand over a coding task to the coder agent",
2158
+ description: "Request to rename a file",
2136
2159
  parameters: [
2137
2160
  {
2138
- name: "agent_name",
2139
- value: "coder"
2140
- },
2141
- {
2142
- name: "task",
2143
- value: "Implement the login feature"
2144
- },
2145
- {
2146
- name: "context",
2147
- value: "We need a secure login system with email and password"
2161
+ name: "source_path",
2162
+ value: "src/old-name.js"
2148
2163
  },
2149
2164
  {
2150
- name: "files",
2151
- value: "src/auth/login.ts,src/auth/types.ts"
2165
+ name: "target_path",
2166
+ value: "src/new-name.js"
2152
2167
  }
2153
2168
  ]
2154
2169
  }
2155
2170
  ],
2156
- permissionLevel: 0 /* None */
2171
+ permissionLevel: 2 /* Write */
2157
2172
  };
2158
- var handler12 = async (_provider, args) => {
2159
- const agentName = getString(args, "agent_name");
2160
- const task = getString(args, "task");
2161
- const context = getString(args, "context", void 0);
2162
- const files = getStringArray(args, "files", []);
2173
+ var handler12 = async (provider, args) => {
2174
+ if (!provider.renameFile) {
2175
+ return {
2176
+ type: "Error" /* Error */,
2177
+ message: "Not possible to rename file. Abort."
2178
+ };
2179
+ }
2180
+ const sourcePath = getString(args, "source_path");
2181
+ const targetPath = getString(args, "target_path");
2182
+ await provider.renameFile(sourcePath, targetPath);
2163
2183
  return {
2164
- type: "HandOver" /* HandOver */,
2165
- agentName,
2166
- task,
2167
- context,
2168
- files
2169
- // originalTask will be set by AgentBase
2184
+ type: "Reply" /* Reply */,
2185
+ message: `<rename_file_path>${targetPath}</rename_file_path><status>Success</status>`
2170
2186
  };
2171
2187
  };
2172
- var isAvailable12 = (_provider) => {
2173
- return true;
2188
+ var isAvailable12 = (provider) => {
2189
+ return !!provider.renameFile;
2174
2190
  };
2175
- var handOver_default = {
2191
+ var renameFile_default = {
2176
2192
  ...toolInfo12,
2177
2193
  handler: handler12,
2178
2194
  isAvailable: isAvailable12
2179
2195
  };
2180
2196
 
2181
- // src/tools/removeFile.ts
2197
+ // src/tools/editFile.ts
2182
2198
  var toolInfo13 = {
2183
- name: "remove_file",
2184
- description: "Request to remove a file at the specified path.",
2199
+ name: "edit_file",
2200
+ description: "Request to edit file contents using before/after text anchors with flexible operations. Supports multiple edit operations in a single call.",
2185
2201
  parameters: [
2186
2202
  {
2187
2203
  name: "path",
2188
- description: "The path of the file to remove",
2204
+ description: "The path of the file to edit",
2189
2205
  required: true,
2190
2206
  usageValue: "File path here"
2207
+ },
2208
+ {
2209
+ name: "operations",
2210
+ description: "Edit operation with before_text, after_text, new_text, and optional line range hints",
2211
+ required: true,
2212
+ allowMultiple: true,
2213
+ children: [
2214
+ {
2215
+ name: "before_text",
2216
+ description: `Text to find as the start anchor (use ${START_OF_FILE} for file start)`,
2217
+ required: false,
2218
+ usageValue: "Text before the edit location"
2219
+ },
2220
+ {
2221
+ name: "after_text",
2222
+ description: `Text to find as the end anchor (use ${END_OF_FILE} for file end)`,
2223
+ required: false,
2224
+ usageValue: "Text after the edit location"
2225
+ },
2226
+ {
2227
+ name: "new_text",
2228
+ description: "Text to replace the content between before_text and after_text",
2229
+ required: true,
2230
+ usageValue: "New text content"
2231
+ },
2232
+ {
2233
+ name: "before_text_line_start",
2234
+ description: "Optional line number hint for before_text location (1-based)",
2235
+ required: false,
2236
+ usageValue: "10"
2237
+ },
2238
+ {
2239
+ name: "after_text_line_start",
2240
+ description: "Optional line number hint for after_text location (1-based)",
2241
+ required: false,
2242
+ usageValue: "20"
2243
+ }
2244
+ ],
2245
+ usageValue: "operations here"
2191
2246
  }
2192
2247
  ],
2193
2248
  examples: [
2194
2249
  {
2195
- description: "Request to remove a file",
2250
+ description: "Replace content between two text anchors",
2196
2251
  parameters: [
2197
2252
  {
2198
2253
  name: "path",
2199
- value: "src/main.js"
2254
+ value: "src/main.ts"
2255
+ },
2256
+ {
2257
+ name: "operations",
2258
+ value: {
2259
+ before_text: "function oldFunction() {",
2260
+ after_text: "}",
2261
+ new_text: '\n return "new implementation";\n'
2262
+ }
2263
+ }
2264
+ ]
2265
+ },
2266
+ {
2267
+ description: "Insert at start of file",
2268
+ parameters: [
2269
+ {
2270
+ name: "path",
2271
+ value: "src/header.ts"
2272
+ },
2273
+ {
2274
+ name: "operations",
2275
+ value: {
2276
+ before_text: START_OF_FILE,
2277
+ after_text: "export",
2278
+ new_text: "// File header comment\n"
2279
+ }
2280
+ }
2281
+ ]
2282
+ },
2283
+ {
2284
+ description: "Multiple operations in one call",
2285
+ parameters: [
2286
+ {
2287
+ name: "path",
2288
+ value: "src/utils.ts"
2289
+ },
2290
+ {
2291
+ name: "operations",
2292
+ value: [
2293
+ {
2294
+ before_text: "import React",
2295
+ after_text: 'from "react"',
2296
+ new_text: ", { useState }"
2297
+ },
2298
+ {
2299
+ before_text: "function Component() {",
2300
+ after_text: "return (",
2301
+ new_text: "\n const [state, setState] = useState(false);\n "
2302
+ }
2303
+ ]
2200
2304
  }
2201
2305
  ]
2202
2306
  }
@@ -2204,57 +2308,130 @@ var toolInfo13 = {
2204
2308
  permissionLevel: 2 /* Write */
2205
2309
  };
2206
2310
  var handler13 = async (provider, args) => {
2207
- if (!provider.removeFile) {
2311
+ if (!provider.readFile || !provider.writeFile) {
2208
2312
  return {
2209
2313
  type: "Error" /* Error */,
2210
- message: "Not possible to remove file. Abort."
2314
+ message: "Not possible to edit file. Abort."
2211
2315
  };
2212
2316
  }
2213
2317
  const path = getString(args, "path");
2214
- await provider.removeFile(path);
2215
- return {
2216
- type: "Reply" /* Reply */,
2217
- message: `<remove_file_path>${path}</remove_file_path><status>Success</status>`
2218
- };
2318
+ const operations = getArray(args, "operations");
2319
+ if (!operations || operations.length === 0) {
2320
+ return {
2321
+ type: "Error" /* Error */,
2322
+ message: `<error><edit_file_path>${path}</edit_file_path><error_message>At least one edit operation is required</error_message></error>`
2323
+ };
2324
+ }
2325
+ const fileContent = await provider.readFile(path);
2326
+ if (fileContent == null) {
2327
+ return {
2328
+ type: "Error" /* Error */,
2329
+ message: `<error><edit_file_path>${path}</edit_file_path><error_message>File not found</error_message></error>`
2330
+ };
2331
+ }
2332
+ try {
2333
+ const result = await editFile(fileContent, operations);
2334
+ await provider.writeFile(path, result);
2335
+ return {
2336
+ type: "Reply" /* Reply */,
2337
+ message: `<edit_file_path>${path}</edit_file_path>`
2338
+ };
2339
+ } catch (error) {
2340
+ return {
2341
+ type: "Error" /* Error */,
2342
+ message: `<error><edit_file_path>${path}</edit_file_path><error_message>${error instanceof Error ? error.message : String(error)}</error_message></error>`
2343
+ };
2344
+ }
2219
2345
  };
2220
2346
  var isAvailable13 = (provider) => {
2221
- return !!provider.removeFile;
2347
+ return !!provider.readFile && !!provider.writeFile;
2222
2348
  };
2223
- var removeFile_default = {
2349
+ var editFile_default = {
2224
2350
  ...toolInfo13,
2225
2351
  handler: handler13,
2226
2352
  isAvailable: isAvailable13
2227
2353
  };
2228
2354
 
2229
- // src/tools/renameFile.ts
2355
+ // src/tools/replaceInFile.ts
2230
2356
  var toolInfo14 = {
2231
- name: "rename_file",
2232
- description: "Request to rename a file from source path to target path.",
2357
+ name: "replace_in_file",
2358
+ description: "Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.",
2233
2359
  parameters: [
2234
2360
  {
2235
- name: "sourcePath",
2236
- description: "The current path of the file",
2361
+ name: "path",
2362
+ description: "The path of the file to modify",
2237
2363
  required: true,
2238
- usageValue: "Source file path here"
2364
+ usageValue: "File path here"
2239
2365
  },
2240
2366
  {
2241
- name: "targetPath",
2242
- description: "The new path for the file",
2367
+ name: "diff",
2368
+ description: `One or more SEARCH/REPLACE blocks following this exact format:
2369
+ \`\`\`
2370
+ <<<<<<< SEARCH
2371
+ [exact content to find]
2372
+ =======
2373
+ [new content to replace with]
2374
+ >>>>>>> REPLACE
2375
+ \`\`\`
2376
+ Critical rules:
2377
+ 1. SEARCH content must match the associated file section to find EXACTLY:
2378
+ * Match character-for-character including whitespace, indentation, line endings
2379
+ * Include all comments, docstrings, etc.
2380
+ 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.
2381
+ * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.
2382
+ * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.
2383
+ * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.
2384
+ 3. Keep SEARCH/REPLACE blocks concise:
2385
+ * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.
2386
+ * Include just the changing lines, and a few surrounding lines if needed for uniqueness.
2387
+ * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.
2388
+ * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.
2389
+ 4. Special operations:
2390
+ * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
2391
+ * To delete code: Use empty REPLACE section`,
2243
2392
  required: true,
2244
- usageValue: "Target file path here"
2393
+ usageValue: "Search and replace blocks here"
2245
2394
  }
2246
2395
  ],
2247
2396
  examples: [
2248
2397
  {
2249
- description: "Request to rename a file",
2398
+ description: "Request to replace sections of content in a file",
2250
2399
  parameters: [
2251
2400
  {
2252
- name: "sourcePath",
2253
- value: "src/old-name.js"
2401
+ name: "path",
2402
+ value: "src/main.js"
2254
2403
  },
2255
2404
  {
2256
- name: "targetPath",
2257
- value: "src/new-name.js"
2405
+ name: "diff",
2406
+ value: `
2407
+ <<<<<<< SEARCH
2408
+ import React from 'react';
2409
+ =======
2410
+ import React, { useState } from 'react';
2411
+ >>>>>>> REPLACE
2412
+
2413
+ <<<<<<< SEARCH
2414
+ function handleSubmit() {
2415
+ saveData();
2416
+ setLoading(false);
2417
+ }
2418
+
2419
+ =======
2420
+ >>>>>>> REPLACE
2421
+
2422
+ <<<<<<< SEARCH
2423
+ return (
2424
+ <div>
2425
+ =======
2426
+ function handleSubmit() {
2427
+ saveData();
2428
+ setLoading(false);
2429
+ }
2430
+
2431
+ return (
2432
+ <div>
2433
+ >>>>>>> REPLACE
2434
+ `
2258
2435
  }
2259
2436
  ]
2260
2437
  }
@@ -2262,24 +2439,32 @@ var toolInfo14 = {
2262
2439
  permissionLevel: 2 /* Write */
2263
2440
  };
2264
2441
  var handler14 = async (provider, args) => {
2265
- if (!provider.renameFile) {
2442
+ if (!provider.readFile || !provider.writeFile) {
2266
2443
  return {
2267
2444
  type: "Error" /* Error */,
2268
- message: "Not possible to rename file. Abort."
2445
+ message: "Not possible to replace in file. Abort."
2269
2446
  };
2270
2447
  }
2271
- const sourcePath = getString(args, "sourcePath");
2272
- const targetPath = getString(args, "targetPath");
2273
- await provider.renameFile(sourcePath, targetPath);
2448
+ const path = getString(args, "path");
2449
+ const diff = getString(args, "diff");
2450
+ const fileContent = await provider.readFile(path);
2451
+ if (fileContent == null) {
2452
+ return {
2453
+ type: "Error" /* Error */,
2454
+ message: `<error><replace_in_file_path>${path}</replace_in_file_path><error_message>File not found</error_message></error>`
2455
+ };
2456
+ }
2457
+ const result = await replaceInFile(fileContent, diff);
2458
+ await provider.writeFile(path, result);
2274
2459
  return {
2275
2460
  type: "Reply" /* Reply */,
2276
- message: `<rename_file_path>${targetPath}</rename_file_path><status>Success</status>`
2461
+ message: `<replace_in_file_path>${path}</replace_in_file_path>`
2277
2462
  };
2278
2463
  };
2279
2464
  var isAvailable14 = (provider) => {
2280
- return !!provider.renameFile;
2465
+ return !!provider.readFile && !!provider.writeFile;
2281
2466
  };
2282
- var renameFile_default = {
2467
+ var replaceInFile_default = {
2283
2468
  ...toolInfo14,
2284
2469
  handler: handler14,
2285
2470
  isAvailable: isAvailable14
@@ -2713,6 +2898,9 @@ ${instance.prompt}`;
2713
2898
  this.config = config;
2714
2899
  this.#policies = policies;
2715
2900
  }
2901
+ get parameters() {
2902
+ return this.ai.options.parameters;
2903
+ }
2716
2904
  get messages() {
2717
2905
  return this.#messages;
2718
2906
  }
@@ -2764,6 +2952,11 @@ ${instance.prompt}`;
2764
2952
  role: "user",
2765
2953
  content: userMessage
2766
2954
  });
2955
+ for (const instance of this.#policies) {
2956
+ if (instance.onBeforeRequest) {
2957
+ await instance.onBeforeRequest(this);
2958
+ }
2959
+ }
2767
2960
  let currentAssistantMessage = "";
2768
2961
  const retryCount = 5;
2769
2962
  for (let i = 0; i < retryCount; i++) {
@@ -2862,7 +3055,7 @@ ${instance.prompt}`;
2862
3055
  }
2863
3056
  case "Delegate" /* Delegate */: {
2864
3057
  if (toolResponses.length > 0) {
2865
- continue;
3058
+ break outer;
2866
3059
  }
2867
3060
  const delegateResp = {
2868
3061
  ...toolResp,
@@ -3279,7 +3472,7 @@ var editingFilesPrompt = (toolNamePrefix) => `
3279
3472
 
3280
3473
  EDITING FILES
3281
3474
 
3282
- You have access to two tools for working with files: **${toolNamePrefix}write_to_file** and **${toolNamePrefix}replace_in_file**. Understanding their roles and selecting the right one for the job will help ensure efficient and accurate modifications.
3475
+ You have two file-manipulation tools: **${toolNamePrefix}write_to_file** (full overwrite) and **${toolNamePrefix}edit_file** (targeted anchor-based edits). Choose the smallest safe operation for every change.
3283
3476
 
3284
3477
  # ${toolNamePrefix}write_to_file
3285
3478
 
@@ -3291,16 +3484,16 @@ You have access to two tools for working with files: **${toolNamePrefix}write_to
3291
3484
 
3292
3485
  - Initial file creation, such as when scaffolding a new project.
3293
3486
  - Overwriting large boilerplate files where you want to replace the entire content at once.
3294
- - When the complexity or number of changes would make ${toolNamePrefix}replace_in_file unwieldy or error-prone.
3487
+ - When the complexity or number of changes would make ${toolNamePrefix}edit_file unwieldy or error-prone.
3295
3488
  - When you need to completely restructure a file's content or change its fundamental organization.
3296
3489
 
3297
3490
  ## Important Considerations
3298
3491
 
3299
3492
  - Using ${toolNamePrefix}write_to_file requires providing the file's complete final content.
3300
- - If you only need to make small changes to an existing file, consider using ${toolNamePrefix}replace_in_file instead to avoid unnecessarily rewriting the entire file.
3493
+ - If you only need to make small changes to an existing file, consider using ${toolNamePrefix}edit_file instead to avoid unnecessarily rewriting the entire file.
3301
3494
  - While ${toolNamePrefix}write_to_file should not be your default choice, don't hesitate to use it when the situation truly calls for it.
3302
3495
 
3303
- # ${toolNamePrefix}replace_in_file
3496
+ # ${toolNamePrefix}edit_file
3304
3497
 
3305
3498
  ## Purpose
3306
3499
 
@@ -3319,10 +3512,10 @@ You have access to two tools for working with files: **${toolNamePrefix}write_to
3319
3512
 
3320
3513
  # Choosing the Appropriate Tool
3321
3514
 
3322
- - **Default to ${toolNamePrefix}replace_in_file** for most changes. It's the safer, more precise option that minimizes potential issues.
3515
+ - **Default to ${toolNamePrefix}edit_file** for most changes. It keeps diffs small and reduces risk.
3323
3516
  - **Use ${toolNamePrefix}write_to_file** when:
3324
3517
  - Creating new files
3325
- - The changes are so extensive that using ${toolNamePrefix}replace_in_file would be more complex or risky
3518
+ - The changes are so extensive that using ${toolNamePrefix}edit_file would be more complex or risky
3326
3519
  - You need to completely reorganize or restructure a file
3327
3520
  - The file is relatively small and the changes affect most of its content
3328
3521
  - You're generating boilerplate or template files
@@ -3330,11 +3523,12 @@ You have access to two tools for working with files: **${toolNamePrefix}write_to
3330
3523
  # Workflow Tips
3331
3524
 
3332
3525
  1. Before editing, assess the scope of your changes and decide which tool to use.
3333
- 2. For targeted edits, apply ${toolNamePrefix}replace_in_file with carefully crafted SEARCH/REPLACE blocks. If you need multiple changes, you can stack multiple SEARCH/REPLACE blocks within a single ${toolNamePrefix}replace_in_file call.
3526
+ 2. For targeted edits, apply ${toolNamePrefix}edit_file with carefully crafted before/after text anchors. If you need multiple changes, you can stack multiple operations within a single ${toolNamePrefix}edit_file call.
3334
3527
  3. For major overhauls or initial file creation, rely on ${toolNamePrefix}write_to_file.
3335
- 4. Once the file has been edited with either ${toolNamePrefix}write_to_file or ${toolNamePrefix}replace_in_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent SEARCH/REPLACE operations, since it reflects any auto-formatting or user-applied changes.
3528
+ 4. Once the file has been edited with either ${toolNamePrefix}write_to_file or ${toolNamePrefix}edit_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent operations, since it reflects any auto-formatting or user-applied changes.
3336
3529
 
3337
- By thoughtfully selecting between ${toolNamePrefix}write_to_file and ${toolNamePrefix}replace_in_file, you can make your file editing process smoother, safer, and more efficient.`;
3530
+ Picking the right tool keeps edits minimal, safe, and easy to review.
3531
+ `;
3338
3532
  var rules = (toolNamePrefix) => `
3339
3533
  ====
3340
3534
 
@@ -3345,10 +3539,10 @@ RULES
3345
3539
  For text files (e.g. README.md), append a footer with the same notice.
3346
3540
  - Never describe what changed inside code comments; comments must focus on purpose or usage only.
3347
3541
  - Before using ${toolNamePrefix}execute_command, consider SYSTEM INFORMATION to ensure commands suit the user's OS. If a command must run in a subdirectory, prepend a single \`cd childDir &&\` segment.
3348
- - Use ${toolNamePrefix}search_files for broad analysis, then ${toolNamePrefix}read_file to inspect context, and finally ${toolNamePrefix}replace_in_file or ${toolNamePrefix}write_to_file to modify.
3349
- - Prefer ${toolNamePrefix}replace_in_file for focused edits; choose ${toolNamePrefix}write_to_file for new files or complete rewrites.
3542
+ - Use ${toolNamePrefix}search_files for broad analysis, then ${toolNamePrefix}read_file to inspect context, and finally ${toolNamePrefix}edit_file or ${toolNamePrefix}write_to_file to modify.
3543
+ - Prefer ${toolNamePrefix}edit_file for focused edits; choose ${toolNamePrefix}write_to_file for new files or complete rewrites.
3350
3544
  - When creating a new file, look for existing files with similar content or patterns; if found, read them and use their structure or conventions as a reference.
3351
- - SEARCH blocks in ${toolNamePrefix}replace_in_file must match whole lines. If multiple blocks are needed, list them in file order.
3545
+ - Use before/after text anchors in ${toolNamePrefix}edit_file to target changes. If multiple operations are needed, list them in file order.
3352
3546
  - Do not guess unseen content. Read existing files first unless creating new ones.
3353
3547
  - Follow existing style, lint, and naming conventions. Ensure all changes compile and pass tests where applicable.
3354
3548
  - ALWAYS wait for the user's confirmation after each tool call before starting the next step.
@@ -3614,6 +3808,7 @@ var configSchema = z.object({
3614
3808
  }).strict();
3615
3809
  var Policies = /* @__PURE__ */ ((Policies2) => {
3616
3810
  Policies2["KnowledgeManagement"] = "knowledgemanagement";
3811
+ Policies2["TruncateContext"] = "truncatecontext";
3617
3812
  return Policies2;
3618
3813
  })(Policies || {});
3619
3814
 
@@ -3761,6 +3956,67 @@ var KnowledgeManagementPolicy = (tools) => {
3761
3956
  };
3762
3957
  };
3763
3958
 
3959
+ // src/Agent/policies/TruncateContext.ts
3960
+ var DEFAULT_MAX_TOKENS_ESTIMATE = 8e3;
3961
+ function getMaxTokens(agent) {
3962
+ const params = agent.parameters || {};
3963
+ if (params.maxTokens && typeof params.maxTokens === "number" && params.maxTokens > 0) {
3964
+ return params.maxTokens;
3965
+ }
3966
+ return DEFAULT_MAX_TOKENS_ESTIMATE;
3967
+ }
3968
+ function estimateTokens(text) {
3969
+ return Math.ceil(text.length / 4);
3970
+ }
3971
+ var TruncateContextPolicy = (tools) => {
3972
+ return {
3973
+ name: "truncatecontext" /* TruncateContext */,
3974
+ async onBeforeRequest(agent) {
3975
+ const messages = agent.messages;
3976
+ if (messages.length < 3) {
3977
+ return;
3978
+ }
3979
+ let totalTokens = 0;
3980
+ for (const msg of messages) {
3981
+ if (typeof msg.content === "string") {
3982
+ totalTokens += estimateTokens(msg.content);
3983
+ } else if (Array.isArray(msg.content)) {
3984
+ for (const block of msg.content) {
3985
+ if (typeof block === "object" && "text" in block && typeof block.text === "string") {
3986
+ totalTokens += estimateTokens(block.text);
3987
+ }
3988
+ }
3989
+ }
3990
+ }
3991
+ const maxTokens = getMaxTokens(agent);
3992
+ if (totalTokens <= maxTokens) {
3993
+ return;
3994
+ }
3995
+ const totalMessages = messages.length;
3996
+ const messagesToKeep = Math.ceil(totalMessages / 2);
3997
+ const minKeep = Math.max(2, messagesToKeep);
3998
+ if (minKeep >= totalMessages) {
3999
+ return;
4000
+ }
4001
+ const keepFromStart = Math.floor(minKeep / 2);
4002
+ const keepFromEnd = minKeep - keepFromStart;
4003
+ const startMessages = messages.slice(0, keepFromStart);
4004
+ const endMessages = messages.slice(-keepFromEnd);
4005
+ const truncatedCount = totalMessages - minKeep;
4006
+ const truncatedMessages = [
4007
+ ...startMessages,
4008
+ // Add a message explaining truncation
4009
+ {
4010
+ role: "user",
4011
+ content: `Note: ${truncatedCount} messages were truncated from the middle to prevent context overflow.`
4012
+ },
4013
+ ...endMessages
4014
+ ];
4015
+ agent.setMessages(truncatedMessages);
4016
+ }
4017
+ };
4018
+ };
4019
+
3764
4020
  // src/Agent/index.ts
3765
4021
  var allAgents = [architectAgentInfo, coderAgentInfo, analyzerAgentInfo, codeFixerAgentInfo];
3766
4022
 
@@ -4159,6 +4415,7 @@ export {
4159
4415
  Policies,
4160
4416
  TaskEventKind,
4161
4417
  ToolResponseType,
4418
+ TruncateContextPolicy,
4162
4419
  UsageMeter,
4163
4420
  agentsPrompt,
4164
4421
  allAgents,
@@ -4181,6 +4438,8 @@ export {
4181
4438
  deepSeekModels,
4182
4439
  defaultModels,
4183
4440
  delegate_default as delegate,
4441
+ editFile_default as editFile,
4442
+ editFile as editFileHelper,
4184
4443
  executeAgentTool,
4185
4444
  executeCommand_default as executeCommand,
4186
4445
  executeTool,
@@ -4194,7 +4453,6 @@ export {
4194
4453
  getString,
4195
4454
  getStringArray,
4196
4455
  handOver_default as handOver,
4197
- listCodeDefinitionNames_default as listCodeDefinitionNames,
4198
4456
  listFiles_default as listFiles,
4199
4457
  makeAgentTool,
4200
4458
  makeTool,