@ddlqhd/agent-sdk 0.1.0 → 0.1.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.
Files changed (48) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-NDSL7NPN.js → chunk-742JTNYI.js} +224 -19
  3. package/dist/chunk-742JTNYI.js.map +1 -0
  4. package/dist/{chunk-5QMA2YBY.cjs → chunk-AJD3DTL7.cjs} +57 -28
  5. package/dist/chunk-AJD3DTL7.cjs.map +1 -0
  6. package/dist/{chunk-X35MHWXE.cjs → chunk-DQFTAD3I.cjs} +231 -24
  7. package/dist/chunk-DQFTAD3I.cjs.map +1 -0
  8. package/dist/{chunk-Q3SOMX26.js → chunk-DXMVWGLJ.js} +52 -25
  9. package/dist/chunk-DXMVWGLJ.js.map +1 -0
  10. package/dist/chunk-LOYIGOBZ.js +54 -0
  11. package/dist/chunk-LOYIGOBZ.js.map +1 -0
  12. package/dist/chunk-OZO7D77N.cjs +59 -0
  13. package/dist/chunk-OZO7D77N.cjs.map +1 -0
  14. package/dist/{chunk-JF5AJQMU.cjs → chunk-Q3L4GIBG.cjs} +211 -58
  15. package/dist/chunk-Q3L4GIBG.cjs.map +1 -0
  16. package/dist/{chunk-OHXW2YM6.js → chunk-THKEF32L.js} +210 -57
  17. package/dist/chunk-THKEF32L.js.map +1 -0
  18. package/dist/cli/index.cjs +36 -37
  19. package/dist/cli/index.cjs.map +1 -1
  20. package/dist/cli/index.js +11 -12
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/{index-DPsZ1zat.d.ts → index-DGPDMbW5.d.cts} +8 -21
  23. package/dist/{index-RTPmFjMp.d.cts → index-nEfayAzD.d.ts} +8 -21
  24. package/dist/index.cjs +91 -79
  25. package/dist/index.d.cts +26 -6
  26. package/dist/index.d.ts +26 -6
  27. package/dist/index.js +4 -4
  28. package/dist/models/index.cjs +15 -15
  29. package/dist/models/index.d.cts +50 -6
  30. package/dist/models/index.d.ts +50 -6
  31. package/dist/models/index.js +2 -2
  32. package/dist/tools/index.cjs +51 -51
  33. package/dist/tools/index.d.cts +3 -3
  34. package/dist/tools/index.d.ts +3 -3
  35. package/dist/tools/index.js +2 -2
  36. package/dist/{types-C0aX_Qdp.d.cts → types-BLf9IqRs.d.cts} +34 -49
  37. package/dist/{types-C0aX_Qdp.d.ts → types-BLf9IqRs.d.ts} +34 -49
  38. package/package.json +15 -4
  39. package/dist/chunk-5QMA2YBY.cjs.map +0 -1
  40. package/dist/chunk-CNSGZVRN.cjs +0 -152
  41. package/dist/chunk-CNSGZVRN.cjs.map +0 -1
  42. package/dist/chunk-JF5AJQMU.cjs.map +0 -1
  43. package/dist/chunk-NDSL7NPN.js.map +0 -1
  44. package/dist/chunk-OHXW2YM6.js.map +0 -1
  45. package/dist/chunk-Q3SOMX26.js.map +0 -1
  46. package/dist/chunk-WH3APNQ5.js +0 -147
  47. package/dist/chunk-WH3APNQ5.js.map +0 -1
  48. package/dist/chunk-X35MHWXE.cjs.map +0 -1
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { zodToJsonSchema } from './chunk-WH3APNQ5.js';
2
+ import { zodToJsonSchema } from './chunk-LOYIGOBZ.js';
3
3
  import { mkdir, writeFile, readFile } from 'fs/promises';
4
- import { join, dirname } from 'path';
4
+ import { join, dirname, win32 } from 'path';
5
5
  import { homedir } from 'os';
6
- import { z } from 'zod';
7
6
  import iconv from 'iconv-lite';
8
7
  import fg from 'fast-glob';
8
+ import { z } from 'zod';
9
9
  import { analyse } from 'chardet';
10
10
  import { execSync, spawn } from 'child_process';
11
11
  import { existsSync } from 'fs';
@@ -223,6 +223,8 @@ var OutputHandler = class {
223
223
  function createOutputHandler(userBasePath) {
224
224
  return new OutputHandler(userBasePath);
225
225
  }
226
+
227
+ // src/tools/registry.ts
226
228
  var ToolRegistry = class {
227
229
  tools = /* @__PURE__ */ new Map();
228
230
  categories = /* @__PURE__ */ new Map();
@@ -381,7 +383,18 @@ var ToolRegistry = class {
381
383
  }
382
384
  let workingInput = rawArgsObj;
383
385
  try {
384
- workingInput = tool.parameters.parse(args);
386
+ const initial = tool.parameters.safeParse(args);
387
+ if (!initial.success) {
388
+ const msg = `Invalid arguments for tool "${name}": ${initial.error.issues.map((i) => i.message).join(", ")}`;
389
+ await hookMgr?.executePostToolUseFailure(
390
+ this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
391
+ errorMessage: msg,
392
+ failureKind: "validation"
393
+ })
394
+ );
395
+ return { content: msg, isError: true };
396
+ }
397
+ workingInput = initial.data;
385
398
  if (hookMgr) {
386
399
  const pre = await hookMgr.executePreToolUse(
387
400
  this.buildHookContext("preToolUse", name, workingInput, options)
@@ -392,21 +405,18 @@ var ToolRegistry = class {
392
405
  isError: true
393
406
  };
394
407
  }
395
- try {
396
- workingInput = tool.parameters.parse(pre.updatedInput ?? workingInput);
397
- } catch (err) {
398
- if (err instanceof z.ZodError) {
399
- const msg = `Invalid arguments after hook merge for tool "${name}": ${err.errors.map((e) => e.message).join(", ")}`;
400
- await hookMgr.executePostToolUseFailure(
401
- this.buildHookContext("postToolUseFailure", name, workingInput, options, {
402
- errorMessage: msg,
403
- failureKind: "validation"
404
- })
405
- );
406
- return { content: msg, isError: true };
407
- }
408
- throw err;
408
+ const merged = tool.parameters.safeParse(pre.updatedInput ?? workingInput);
409
+ if (!merged.success) {
410
+ const msg = `Invalid arguments after hook merge for tool "${name}": ${merged.error.issues.map((i) => i.message).join(", ")}`;
411
+ await hookMgr.executePostToolUseFailure(
412
+ this.buildHookContext("postToolUseFailure", name, workingInput, options, {
413
+ errorMessage: msg,
414
+ failureKind: "validation"
415
+ })
416
+ );
417
+ return { content: msg, isError: true };
409
418
  }
419
+ workingInput = merged.data;
410
420
  }
411
421
  const handlerArgs = workingInput;
412
422
  const executionContext = {
@@ -442,19 +452,6 @@ var ToolRegistry = class {
442
452
  );
443
453
  return finalResult;
444
454
  } catch (error) {
445
- if (error instanceof z.ZodError) {
446
- const msg2 = `Invalid arguments for tool "${name}": ${error.errors.map((e) => e.message).join(", ")}`;
447
- await hookMgr?.executePostToolUseFailure(
448
- this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
449
- errorMessage: msg2,
450
- failureKind: "validation"
451
- })
452
- );
453
- return {
454
- content: msg2,
455
- isError: true
456
- };
457
- }
458
455
  const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
459
456
  await hookMgr?.executePostToolUseFailure(
460
457
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
@@ -802,10 +799,94 @@ var MAX_LINE_LENGTH = 2e3;
802
799
  var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
803
800
  var MAX_BYTES = 50 * 1024;
804
801
  var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
802
+ var EDIT_MAX_FILE_BYTES = 1024 ** 3;
803
+ function detectDominantEol(content) {
804
+ let crlf = 0;
805
+ for (let i = 0; i < content.length - 1; i++) {
806
+ if (content[i] === "\r" && content[i + 1] === "\n") {
807
+ crlf++;
808
+ }
809
+ }
810
+ let nl = 0;
811
+ for (let i = 0; i < content.length; i++) {
812
+ if (content[i] === "\n") {
813
+ nl++;
814
+ }
815
+ }
816
+ const bareLf = nl - crlf;
817
+ if (crlf > bareLf) {
818
+ return "\r\n";
819
+ }
820
+ return "\n";
821
+ }
822
+ function normalizeNewStringEols(text, eol) {
823
+ const unified = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
824
+ if (eol === "\n") {
825
+ return unified;
826
+ }
827
+ return unified.replace(/\n/g, "\r\n");
828
+ }
829
+ function buildNeedleCandidates(oldString, dominantEol) {
830
+ const out = [oldString];
831
+ if (dominantEol === "\r\n") {
832
+ if (!oldString.includes("\r")) {
833
+ const v = oldString.replace(/\n/g, "\r\n");
834
+ if (v !== oldString) {
835
+ out.push(v);
836
+ }
837
+ }
838
+ } else {
839
+ if (oldString.includes("\r\n")) {
840
+ const v = oldString.replace(/\r\n/g, "\n");
841
+ if (v !== oldString) {
842
+ out.push(v);
843
+ }
844
+ } else if (oldString.includes("\r")) {
845
+ const v = oldString.replace(/\r/g, "\n");
846
+ if (v !== oldString) {
847
+ out.push(v);
848
+ }
849
+ }
850
+ }
851
+ return out;
852
+ }
853
+ function countOccurrences(haystack, needle) {
854
+ if (needle.length === 0) {
855
+ return 0;
856
+ }
857
+ let n = 0;
858
+ let from = 0;
859
+ let i = 0;
860
+ while ((i = haystack.indexOf(needle, from)) !== -1) {
861
+ n++;
862
+ from = i + needle.length;
863
+ }
864
+ return n;
865
+ }
866
+ function replaceNonOverlapping(content, needle, replacement, replaceAll) {
867
+ if (!replaceAll) {
868
+ const i2 = content.indexOf(needle);
869
+ if (i2 === -1) {
870
+ throw new Error("Edit: needle not found after resolution (internal inconsistency)");
871
+ }
872
+ return content.slice(0, i2) + replacement + content.slice(i2 + needle.length);
873
+ }
874
+ let out = "";
875
+ let from = 0;
876
+ let i = 0;
877
+ while ((i = content.indexOf(needle, from)) !== -1) {
878
+ out += content.slice(from, i) + replacement;
879
+ from = i + needle.length;
880
+ }
881
+ out += content.slice(from);
882
+ return out;
883
+ }
805
884
  var readFileTool = createTool({
806
885
  name: "Read",
807
886
  category: "filesystem",
808
- description: `Reads a file from the local filesystem. You can access any file directly by using this tool.
887
+ description: `Reads human-readable text from the local filesystem: source code, configuration, logs, Markdown, and structured text (JSON, XML, YAML, etc.).
888
+
889
+ Do NOT use Read for binary formats. This tool decodes the file as text; for binaries the result is garbage or misleading. Examples: images (png, jpg, gif, webp, ico), Office/OpenXML (xlsx, docx, pptx), PDF, audio/video, archives (zip, gz, etc.), and compiled binaries. Prefer format-specific tooling, a small script or library in the user's environment, or ask the user for an exported/plain-text view.
809
890
 
810
891
  Usage:
811
892
  - The file_path parameter must be an absolute path, not a relative path
@@ -814,10 +895,12 @@ Usage:
814
895
  - Results are returned using cat -n style, with line numbers starting at 1
815
896
  - Lines longer than 2000 characters are truncated
816
897
  - Use the offset and limit parameters to read specific line ranges of large files
817
- - If you read a file that exists but has empty contents you will receive an error message
898
+ - An empty file (no lines) returns successfully with no numbered lines and a suffix noting zero total lines\u2014it is not an error
818
899
  - Text encoding is detected automatically from the file (BOM, UTF-8 validity, charset analysis). You rarely need to set encoding; use it only to override detection (e.g. gbk, gb18030, cp936 maps to gbk)`,
819
900
  parameters: z.object({
820
- file_path: z.string().describe("The absolute path to the file to read"),
901
+ file_path: z.string().describe(
902
+ "Absolute path to a text or text-decodable source file (not binary formats such as images, xlsx, or pdf)."
903
+ ),
821
904
  encoding: z.string().optional().describe(
822
905
  'Optional. Omit or use "auto" for automatic detection (default). Set only to force a specific encoding (utf8, gbk, gb18030, latin1, etc.; cp936 is treated as gbk).'
823
906
  ),
@@ -988,7 +1071,9 @@ var editTool = createTool({
988
1071
 
989
1072
  Usage:
990
1073
  - You must use the Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file
1074
+ - Files at or above 1 GiB cannot be edited with this tool (use another workflow for huge files)
991
1075
  - When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: line number + tab. Everything after that is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string
1076
+ - old_string may use \\n line breaks; CRLF files are matched and new_string is rewritten to the file's dominant line ending style (\\r\\n vs \\n)
992
1077
  - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required
993
1078
  - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked
994
1079
  - The edit will FAIL if old_string is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all to change every instance of old_string
@@ -996,7 +1081,7 @@ Usage:
996
1081
  - For non-UTF-8 files, set encoding to the same value you used with Read (default is utf8)`,
997
1082
  parameters: z.object({
998
1083
  file_path: z.string().describe("The absolute path to the file to modify"),
999
- old_string: z.string().describe("The text to replace"),
1084
+ old_string: z.string().min(1).describe("The text to replace (non-empty)"),
1000
1085
  new_string: z.string().describe("The text to replace it with (must be different from old_string)"),
1001
1086
  replace_all: z.boolean().default(false).describe("Replace all occurrences of old_string (default false)"),
1002
1087
  encoding: z.string().optional().describe(
@@ -1018,25 +1103,46 @@ Usage:
1018
1103
  isError: true
1019
1104
  };
1020
1105
  }
1106
+ const fs = await import('fs/promises');
1107
+ const stat = await fs.stat(file_path);
1108
+ if (!stat.isFile()) {
1109
+ return {
1110
+ content: `Error: ${file_path} is not a file`,
1111
+ isError: true
1112
+ };
1113
+ }
1114
+ if (stat.size >= EDIT_MAX_FILE_BYTES) {
1115
+ return {
1116
+ content: `Error: file is too large to edit (${stat.size} bytes). Maximum size is ${EDIT_MAX_FILE_BYTES} bytes (1 GiB). Use a different tool or split the work.`,
1117
+ isError: true
1118
+ };
1119
+ }
1021
1120
  const content = await readFileAsUnicodeString(file_path, normalized);
1022
- if (!content.includes(old_string)) {
1121
+ const dominantEol = detectDominantEol(content);
1122
+ const candidates = buildNeedleCandidates(old_string, dominantEol);
1123
+ let needle = null;
1124
+ for (const c of candidates) {
1125
+ if (countOccurrences(content, c) > 0) {
1126
+ needle = c;
1127
+ break;
1128
+ }
1129
+ }
1130
+ if (needle === null) {
1023
1131
  return {
1024
1132
  content: `old_string not found in ${file_path}`,
1025
1133
  isError: true
1026
1134
  };
1027
1135
  }
1028
- if (!replace_all) {
1029
- const occurrences2 = content.split(old_string).length - 1;
1030
- if (occurrences2 > 1) {
1031
- return {
1032
- content: `Found ${occurrences2} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1033
- isError: true
1034
- };
1035
- }
1136
+ const occurrences = countOccurrences(content, needle);
1137
+ if (!replace_all && occurrences > 1) {
1138
+ return {
1139
+ content: `Found ${occurrences} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1140
+ isError: true
1141
+ };
1036
1142
  }
1037
- const newContent = replace_all ? content.replaceAll(old_string, new_string) : content.replace(old_string, new_string);
1143
+ const normalizedNew = normalizeNewStringEols(new_string, dominantEol);
1144
+ const newContent = replaceNonOverlapping(content, needle, normalizedNew, replace_all);
1038
1145
  await writeFileFromUnicodeString(file_path, newContent, normalized);
1039
- const occurrences = replace_all ? content.split(old_string).length - 1 : 1;
1040
1146
  return {
1041
1147
  content: `Successfully edited ${file_path} (${occurrences} replacement${occurrences > 1 ? "s" : ""})`
1042
1148
  };
@@ -1118,6 +1224,53 @@ function findInPath(executable) {
1118
1224
  return null;
1119
1225
  }
1120
1226
  }
1227
+ function findGitBashNextToGitExe(gitExe) {
1228
+ if (!existsSync(gitExe)) {
1229
+ return null;
1230
+ }
1231
+ const dir = win32.dirname(gitExe);
1232
+ const siblingBash = win32.join(dir, "bash.exe");
1233
+ if (existsSync(siblingBash)) {
1234
+ return siblingBash;
1235
+ }
1236
+ let walk = dir;
1237
+ for (let i = 0; i < 4; i++) {
1238
+ const bashPath = win32.join(walk, "bin", "bash.exe");
1239
+ if (existsSync(bashPath)) {
1240
+ return bashPath;
1241
+ }
1242
+ const parent = win32.dirname(walk);
1243
+ if (parent === walk) {
1244
+ break;
1245
+ }
1246
+ walk = parent;
1247
+ }
1248
+ return null;
1249
+ }
1250
+ function findBashViaGitInstall() {
1251
+ const gitExe = findInPath("git");
1252
+ if (!gitExe) {
1253
+ return null;
1254
+ }
1255
+ return findGitBashNextToGitExe(gitExe);
1256
+ }
1257
+ function findGitBashInDefaultInstallDirs() {
1258
+ const drives = ["C", "D", "E"];
1259
+ const relatives = [
1260
+ ["Program Files", "Git", "bin", "bash.exe"],
1261
+ ["Program Files (x86)", "Git", "bin", "bash.exe"]
1262
+ ];
1263
+ for (const drive of drives) {
1264
+ const root = `${drive}:\\`;
1265
+ for (const parts of relatives) {
1266
+ const p = win32.join(root, ...parts);
1267
+ if (existsSync(p)) {
1268
+ return p;
1269
+ }
1270
+ }
1271
+ }
1272
+ return null;
1273
+ }
1121
1274
  function getShellPath() {
1122
1275
  if (cachedShellPath !== null) {
1123
1276
  return cachedShellPath;
@@ -1128,15 +1281,15 @@ function getShellPath() {
1128
1281
  cachedShellPath = bashPath;
1129
1282
  return bashPath;
1130
1283
  }
1131
- const gitBashPaths = [
1132
- "C:\\Program Files\\Git\\bin\\bash.exe",
1133
- "C:\\Program Files (x86)\\Git\\bin\\bash.exe"
1134
- ];
1135
- for (const path of gitBashPaths) {
1136
- if (existsSync(path)) {
1137
- cachedShellPath = path;
1138
- return path;
1139
- }
1284
+ const viaGit = findBashViaGitInstall();
1285
+ if (viaGit) {
1286
+ cachedShellPath = viaGit;
1287
+ return viaGit;
1288
+ }
1289
+ const fromDirs = findGitBashInDefaultInstallDirs();
1290
+ if (fromDirs) {
1291
+ cachedShellPath = fromDirs;
1292
+ return fromDirs;
1140
1293
  }
1141
1294
  const pwshPath = findInPath("pwsh");
1142
1295
  if (pwshPath) {
@@ -2704,5 +2857,5 @@ function createFunctionHook(config) {
2704
2857
  }
2705
2858
 
2706
2859
  export { DEFAULT_GREP_HEAD_LIMIT, FileStorageStrategy, HookManager, MAX_LINE_LENGTH2 as MAX_LINE_LENGTH, OUTPUT_CONFIG, OutputHandler, PaginationHintStrategy, SmartTruncateStrategy, ToolRegistry, agentTool, bashTool, buildHookEnv, createAgentTool, createAskUserQuestionTool, createFunctionHook, createOutputHandler, createSkillTool, createTool, editTool, formatAnswerSummary, formatAskUserQuestionPrompt, formatEnvironmentSection, getAllBuiltinTools, getEnvironmentInfo, getFileSystemTools, getGlobalRegistry, getGrepTools, getInteractionTools, getSafeBuiltinTools, getShellTools, getSkillTools, getSubagentTools, getTaskTools, getWebTools, globTool, grepTool, loadHooksSettingsFromProject, loadHooksSettingsFromUser, matchTool, mergeCommandHookLayers, parseHooksSettingsFile, questionTool, readFileTool, subagentRequestSchema, taskCreateTool, taskListTool, taskUpdateTool, truncateMatchLineForDisplay, webFetchTool, webSearchTool, writeFileTool };
2707
- //# sourceMappingURL=chunk-OHXW2YM6.js.map
2708
- //# sourceMappingURL=chunk-OHXW2YM6.js.map
2860
+ //# sourceMappingURL=chunk-THKEF32L.js.map
2861
+ //# sourceMappingURL=chunk-THKEF32L.js.map