@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,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var chunkCNSGZVRN_cjs = require('./chunk-CNSGZVRN.cjs');
4
+ var chunkOZO7D77N_cjs = require('./chunk-OZO7D77N.cjs');
5
5
  var promises = require('fs/promises');
6
6
  var path = require('path');
7
7
  var os = require('os');
8
- var zod = require('zod');
9
8
  var iconv = require('iconv-lite');
10
9
  var fg = require('fast-glob');
10
+ var zod = require('zod');
11
11
  var chardet = require('chardet');
12
12
  var child_process = require('child_process');
13
13
  var fs = require('fs');
@@ -253,6 +253,8 @@ var OutputHandler = class {
253
253
  function createOutputHandler(userBasePath) {
254
254
  return new OutputHandler(userBasePath);
255
255
  }
256
+
257
+ // src/tools/registry.ts
256
258
  var ToolRegistry = class {
257
259
  tools = /* @__PURE__ */ new Map();
258
260
  categories = /* @__PURE__ */ new Map();
@@ -411,7 +413,18 @@ var ToolRegistry = class {
411
413
  }
412
414
  let workingInput = rawArgsObj;
413
415
  try {
414
- workingInput = tool.parameters.parse(args);
416
+ const initial = tool.parameters.safeParse(args);
417
+ if (!initial.success) {
418
+ const msg = `Invalid arguments for tool "${name}": ${initial.error.issues.map((i) => i.message).join(", ")}`;
419
+ await hookMgr?.executePostToolUseFailure(
420
+ this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
421
+ errorMessage: msg,
422
+ failureKind: "validation"
423
+ })
424
+ );
425
+ return { content: msg, isError: true };
426
+ }
427
+ workingInput = initial.data;
415
428
  if (hookMgr) {
416
429
  const pre = await hookMgr.executePreToolUse(
417
430
  this.buildHookContext("preToolUse", name, workingInput, options)
@@ -422,21 +435,18 @@ var ToolRegistry = class {
422
435
  isError: true
423
436
  };
424
437
  }
425
- try {
426
- workingInput = tool.parameters.parse(pre.updatedInput ?? workingInput);
427
- } catch (err) {
428
- if (err instanceof zod.z.ZodError) {
429
- const msg = `Invalid arguments after hook merge for tool "${name}": ${err.errors.map((e) => e.message).join(", ")}`;
430
- await hookMgr.executePostToolUseFailure(
431
- this.buildHookContext("postToolUseFailure", name, workingInput, options, {
432
- errorMessage: msg,
433
- failureKind: "validation"
434
- })
435
- );
436
- return { content: msg, isError: true };
437
- }
438
- throw err;
438
+ const merged = tool.parameters.safeParse(pre.updatedInput ?? workingInput);
439
+ if (!merged.success) {
440
+ const msg = `Invalid arguments after hook merge for tool "${name}": ${merged.error.issues.map((i) => i.message).join(", ")}`;
441
+ await hookMgr.executePostToolUseFailure(
442
+ this.buildHookContext("postToolUseFailure", name, workingInput, options, {
443
+ errorMessage: msg,
444
+ failureKind: "validation"
445
+ })
446
+ );
447
+ return { content: msg, isError: true };
439
448
  }
449
+ workingInput = merged.data;
440
450
  }
441
451
  const handlerArgs = workingInput;
442
452
  const executionContext = {
@@ -472,19 +482,6 @@ var ToolRegistry = class {
472
482
  );
473
483
  return finalResult;
474
484
  } catch (error) {
475
- if (error instanceof zod.z.ZodError) {
476
- const msg2 = `Invalid arguments for tool "${name}": ${error.errors.map((e) => e.message).join(", ")}`;
477
- await hookMgr?.executePostToolUseFailure(
478
- this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
479
- errorMessage: msg2,
480
- failureKind: "validation"
481
- })
482
- );
483
- return {
484
- content: msg2,
485
- isError: true
486
- };
487
- }
488
485
  const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
489
486
  await hookMgr?.executePostToolUseFailure(
490
487
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
@@ -505,7 +502,7 @@ var ToolRegistry = class {
505
502
  return this.getAll().map((tool) => ({
506
503
  name: tool.name,
507
504
  description: tool.description,
508
- parameters: chunkCNSGZVRN_cjs.zodToJsonSchema(tool.parameters)
505
+ parameters: chunkOZO7D77N_cjs.zodToJsonSchema(tool.parameters)
509
506
  }));
510
507
  }
511
508
  /**
@@ -561,7 +558,7 @@ var ToolRegistry = class {
561
558
  return this.getAll().map((tool) => ({
562
559
  name: tool.name,
563
560
  description: tool.description,
564
- parameters: chunkCNSGZVRN_cjs.zodToJsonSchema(tool.parameters)
561
+ parameters: chunkOZO7D77N_cjs.zodToJsonSchema(tool.parameters)
565
562
  }));
566
563
  }
567
564
  };
@@ -832,10 +829,94 @@ var MAX_LINE_LENGTH = 2e3;
832
829
  var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
833
830
  var MAX_BYTES = 50 * 1024;
834
831
  var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
832
+ var EDIT_MAX_FILE_BYTES = 1024 ** 3;
833
+ function detectDominantEol(content) {
834
+ let crlf = 0;
835
+ for (let i = 0; i < content.length - 1; i++) {
836
+ if (content[i] === "\r" && content[i + 1] === "\n") {
837
+ crlf++;
838
+ }
839
+ }
840
+ let nl = 0;
841
+ for (let i = 0; i < content.length; i++) {
842
+ if (content[i] === "\n") {
843
+ nl++;
844
+ }
845
+ }
846
+ const bareLf = nl - crlf;
847
+ if (crlf > bareLf) {
848
+ return "\r\n";
849
+ }
850
+ return "\n";
851
+ }
852
+ function normalizeNewStringEols(text, eol) {
853
+ const unified = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
854
+ if (eol === "\n") {
855
+ return unified;
856
+ }
857
+ return unified.replace(/\n/g, "\r\n");
858
+ }
859
+ function buildNeedleCandidates(oldString, dominantEol) {
860
+ const out = [oldString];
861
+ if (dominantEol === "\r\n") {
862
+ if (!oldString.includes("\r")) {
863
+ const v = oldString.replace(/\n/g, "\r\n");
864
+ if (v !== oldString) {
865
+ out.push(v);
866
+ }
867
+ }
868
+ } else {
869
+ if (oldString.includes("\r\n")) {
870
+ const v = oldString.replace(/\r\n/g, "\n");
871
+ if (v !== oldString) {
872
+ out.push(v);
873
+ }
874
+ } else if (oldString.includes("\r")) {
875
+ const v = oldString.replace(/\r/g, "\n");
876
+ if (v !== oldString) {
877
+ out.push(v);
878
+ }
879
+ }
880
+ }
881
+ return out;
882
+ }
883
+ function countOccurrences(haystack, needle) {
884
+ if (needle.length === 0) {
885
+ return 0;
886
+ }
887
+ let n = 0;
888
+ let from = 0;
889
+ let i = 0;
890
+ while ((i = haystack.indexOf(needle, from)) !== -1) {
891
+ n++;
892
+ from = i + needle.length;
893
+ }
894
+ return n;
895
+ }
896
+ function replaceNonOverlapping(content, needle, replacement, replaceAll) {
897
+ if (!replaceAll) {
898
+ const i2 = content.indexOf(needle);
899
+ if (i2 === -1) {
900
+ throw new Error("Edit: needle not found after resolution (internal inconsistency)");
901
+ }
902
+ return content.slice(0, i2) + replacement + content.slice(i2 + needle.length);
903
+ }
904
+ let out = "";
905
+ let from = 0;
906
+ let i = 0;
907
+ while ((i = content.indexOf(needle, from)) !== -1) {
908
+ out += content.slice(from, i) + replacement;
909
+ from = i + needle.length;
910
+ }
911
+ out += content.slice(from);
912
+ return out;
913
+ }
835
914
  var readFileTool = createTool({
836
915
  name: "Read",
837
916
  category: "filesystem",
838
- description: `Reads a file from the local filesystem. You can access any file directly by using this tool.
917
+ description: `Reads human-readable text from the local filesystem: source code, configuration, logs, Markdown, and structured text (JSON, XML, YAML, etc.).
918
+
919
+ 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.
839
920
 
840
921
  Usage:
841
922
  - The file_path parameter must be an absolute path, not a relative path
@@ -844,10 +925,12 @@ Usage:
844
925
  - Results are returned using cat -n style, with line numbers starting at 1
845
926
  - Lines longer than 2000 characters are truncated
846
927
  - Use the offset and limit parameters to read specific line ranges of large files
847
- - If you read a file that exists but has empty contents you will receive an error message
928
+ - An empty file (no lines) returns successfully with no numbered lines and a suffix noting zero total lines\u2014it is not an error
848
929
  - 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)`,
849
930
  parameters: zod.z.object({
850
- file_path: zod.z.string().describe("The absolute path to the file to read"),
931
+ file_path: zod.z.string().describe(
932
+ "Absolute path to a text or text-decodable source file (not binary formats such as images, xlsx, or pdf)."
933
+ ),
851
934
  encoding: zod.z.string().optional().describe(
852
935
  '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).'
853
936
  ),
@@ -1018,7 +1101,9 @@ var editTool = createTool({
1018
1101
 
1019
1102
  Usage:
1020
1103
  - 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
1104
+ - Files at or above 1 GiB cannot be edited with this tool (use another workflow for huge files)
1021
1105
  - 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
1106
+ - 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)
1022
1107
  - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required
1023
1108
  - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked
1024
1109
  - 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
@@ -1026,7 +1111,7 @@ Usage:
1026
1111
  - For non-UTF-8 files, set encoding to the same value you used with Read (default is utf8)`,
1027
1112
  parameters: zod.z.object({
1028
1113
  file_path: zod.z.string().describe("The absolute path to the file to modify"),
1029
- old_string: zod.z.string().describe("The text to replace"),
1114
+ old_string: zod.z.string().min(1).describe("The text to replace (non-empty)"),
1030
1115
  new_string: zod.z.string().describe("The text to replace it with (must be different from old_string)"),
1031
1116
  replace_all: zod.z.boolean().default(false).describe("Replace all occurrences of old_string (default false)"),
1032
1117
  encoding: zod.z.string().optional().describe(
@@ -1048,25 +1133,46 @@ Usage:
1048
1133
  isError: true
1049
1134
  };
1050
1135
  }
1136
+ const fs = await import('fs/promises');
1137
+ const stat = await fs.stat(file_path);
1138
+ if (!stat.isFile()) {
1139
+ return {
1140
+ content: `Error: ${file_path} is not a file`,
1141
+ isError: true
1142
+ };
1143
+ }
1144
+ if (stat.size >= EDIT_MAX_FILE_BYTES) {
1145
+ return {
1146
+ 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.`,
1147
+ isError: true
1148
+ };
1149
+ }
1051
1150
  const content = await readFileAsUnicodeString(file_path, normalized);
1052
- if (!content.includes(old_string)) {
1151
+ const dominantEol = detectDominantEol(content);
1152
+ const candidates = buildNeedleCandidates(old_string, dominantEol);
1153
+ let needle = null;
1154
+ for (const c of candidates) {
1155
+ if (countOccurrences(content, c) > 0) {
1156
+ needle = c;
1157
+ break;
1158
+ }
1159
+ }
1160
+ if (needle === null) {
1053
1161
  return {
1054
1162
  content: `old_string not found in ${file_path}`,
1055
1163
  isError: true
1056
1164
  };
1057
1165
  }
1058
- if (!replace_all) {
1059
- const occurrences2 = content.split(old_string).length - 1;
1060
- if (occurrences2 > 1) {
1061
- return {
1062
- content: `Found ${occurrences2} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1063
- isError: true
1064
- };
1065
- }
1166
+ const occurrences = countOccurrences(content, needle);
1167
+ if (!replace_all && occurrences > 1) {
1168
+ return {
1169
+ content: `Found ${occurrences} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1170
+ isError: true
1171
+ };
1066
1172
  }
1067
- const newContent = replace_all ? content.replaceAll(old_string, new_string) : content.replace(old_string, new_string);
1173
+ const normalizedNew = normalizeNewStringEols(new_string, dominantEol);
1174
+ const newContent = replaceNonOverlapping(content, needle, normalizedNew, replace_all);
1068
1175
  await writeFileFromUnicodeString(file_path, newContent, normalized);
1069
- const occurrences = replace_all ? content.split(old_string).length - 1 : 1;
1070
1176
  return {
1071
1177
  content: `Successfully edited ${file_path} (${occurrences} replacement${occurrences > 1 ? "s" : ""})`
1072
1178
  };
@@ -1148,6 +1254,53 @@ function findInPath(executable) {
1148
1254
  return null;
1149
1255
  }
1150
1256
  }
1257
+ function findGitBashNextToGitExe(gitExe) {
1258
+ if (!fs.existsSync(gitExe)) {
1259
+ return null;
1260
+ }
1261
+ const dir = path.win32.dirname(gitExe);
1262
+ const siblingBash = path.win32.join(dir, "bash.exe");
1263
+ if (fs.existsSync(siblingBash)) {
1264
+ return siblingBash;
1265
+ }
1266
+ let walk = dir;
1267
+ for (let i = 0; i < 4; i++) {
1268
+ const bashPath = path.win32.join(walk, "bin", "bash.exe");
1269
+ if (fs.existsSync(bashPath)) {
1270
+ return bashPath;
1271
+ }
1272
+ const parent = path.win32.dirname(walk);
1273
+ if (parent === walk) {
1274
+ break;
1275
+ }
1276
+ walk = parent;
1277
+ }
1278
+ return null;
1279
+ }
1280
+ function findBashViaGitInstall() {
1281
+ const gitExe = findInPath("git");
1282
+ if (!gitExe) {
1283
+ return null;
1284
+ }
1285
+ return findGitBashNextToGitExe(gitExe);
1286
+ }
1287
+ function findGitBashInDefaultInstallDirs() {
1288
+ const drives = ["C", "D", "E"];
1289
+ const relatives = [
1290
+ ["Program Files", "Git", "bin", "bash.exe"],
1291
+ ["Program Files (x86)", "Git", "bin", "bash.exe"]
1292
+ ];
1293
+ for (const drive of drives) {
1294
+ const root = `${drive}:\\`;
1295
+ for (const parts of relatives) {
1296
+ const p = path.win32.join(root, ...parts);
1297
+ if (fs.existsSync(p)) {
1298
+ return p;
1299
+ }
1300
+ }
1301
+ }
1302
+ return null;
1303
+ }
1151
1304
  function getShellPath() {
1152
1305
  if (cachedShellPath !== null) {
1153
1306
  return cachedShellPath;
@@ -1158,15 +1311,15 @@ function getShellPath() {
1158
1311
  cachedShellPath = bashPath;
1159
1312
  return bashPath;
1160
1313
  }
1161
- const gitBashPaths = [
1162
- "C:\\Program Files\\Git\\bin\\bash.exe",
1163
- "C:\\Program Files (x86)\\Git\\bin\\bash.exe"
1164
- ];
1165
- for (const path of gitBashPaths) {
1166
- if (fs.existsSync(path)) {
1167
- cachedShellPath = path;
1168
- return path;
1169
- }
1314
+ const viaGit = findBashViaGitInstall();
1315
+ if (viaGit) {
1316
+ cachedShellPath = viaGit;
1317
+ return viaGit;
1318
+ }
1319
+ const fromDirs = findGitBashInDefaultInstallDirs();
1320
+ if (fromDirs) {
1321
+ cachedShellPath = fromDirs;
1322
+ return fromDirs;
1170
1323
  }
1171
1324
  const pwshPath = findInPath("pwsh");
1172
1325
  if (pwshPath) {
@@ -2784,5 +2937,5 @@ exports.truncateMatchLineForDisplay = truncateMatchLineForDisplay;
2784
2937
  exports.webFetchTool = webFetchTool;
2785
2938
  exports.webSearchTool = webSearchTool;
2786
2939
  exports.writeFileTool = writeFileTool;
2787
- //# sourceMappingURL=chunk-JF5AJQMU.cjs.map
2788
- //# sourceMappingURL=chunk-JF5AJQMU.cjs.map
2940
+ //# sourceMappingURL=chunk-Q3L4GIBG.cjs.map
2941
+ //# sourceMappingURL=chunk-Q3L4GIBG.cjs.map