@ljoukov/llm 3.0.11 → 3.0.13

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.cjs CHANGED
@@ -68,10 +68,10 @@ __export(index_exports, {
68
68
  createListDirectoryTool: () => createListDirectoryTool,
69
69
  createModelAgnosticFilesystemToolSet: () => createModelAgnosticFilesystemToolSet,
70
70
  createNodeAgentFilesystem: () => createNodeAgentFilesystem,
71
- createReadFilesTool: () => createReadFilesTool,
72
71
  createReplaceTool: () => createReplaceTool,
73
72
  createRgSearchTool: () => createRgSearchTool,
74
73
  createToolLoopSteeringChannel: () => createToolLoopSteeringChannel,
74
+ createViewImageTool: () => createViewImageTool,
75
75
  createWriteFileTool: () => createWriteFileTool,
76
76
  customTool: () => customTool,
77
77
  encodeChatGptAuthJson: () => encodeChatGptAuthJson,
@@ -2218,7 +2218,6 @@ function getGoogleAuthOptions(scopes) {
2218
2218
 
2219
2219
  // src/google/client.ts
2220
2220
  var GEMINI_TEXT_MODEL_IDS = [
2221
- "gemini-3-pro-preview",
2222
2221
  "gemini-3.1-pro-preview",
2223
2222
  "gemini-3-flash-preview",
2224
2223
  "gemini-2.5-pro",
@@ -4022,6 +4021,38 @@ function mergeToolOutput(value) {
4022
4021
  return JSON.stringify({ error: "Failed to serialize tool output", detail: message });
4023
4022
  }
4024
4023
  }
4024
+ function isLlmToolOutputContentItem(value) {
4025
+ if (!isPlainRecord(value)) {
4026
+ return false;
4027
+ }
4028
+ const itemType = typeof value.type === "string" ? value.type : "";
4029
+ if (itemType === "input_text") {
4030
+ return typeof value.text === "string";
4031
+ }
4032
+ if (itemType === "input_image") {
4033
+ return typeof value.image_url === "string";
4034
+ }
4035
+ if (itemType === "input_file") {
4036
+ const keys = ["file_data", "file_id", "file_url", "filename"];
4037
+ for (const key of keys) {
4038
+ const part = value[key];
4039
+ if (part !== void 0 && part !== null && typeof part !== "string") {
4040
+ return false;
4041
+ }
4042
+ }
4043
+ return true;
4044
+ }
4045
+ return false;
4046
+ }
4047
+ function toOpenAiToolOutput(value) {
4048
+ if (isLlmToolOutputContentItem(value)) {
4049
+ return [value];
4050
+ }
4051
+ if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
4052
+ return value;
4053
+ }
4054
+ return mergeToolOutput(value);
4055
+ }
4025
4056
  function parseOpenAiToolArguments(raw) {
4026
4057
  const trimmed = raw.trim();
4027
4058
  if (trimmed.length === 0) {
@@ -4377,7 +4408,6 @@ function resolveGeminiThinkingConfig(modelId) {
4377
4408
  return void 0;
4378
4409
  }
4379
4410
  switch (modelId) {
4380
- case "gemini-3-pro-preview":
4381
4411
  case "gemini-3.1-pro-preview":
4382
4412
  return { includeThoughts: true };
4383
4413
  case "gemini-3-flash-preview":
@@ -5379,13 +5409,13 @@ async function runToolLoop(request) {
5379
5409
  toolOutputs.push({
5380
5410
  type: "custom_tool_call_output",
5381
5411
  call_id: entry.call.call_id,
5382
- output: mergeToolOutput(outputPayload)
5412
+ output: toOpenAiToolOutput(outputPayload)
5383
5413
  });
5384
5414
  } else {
5385
5415
  toolOutputs.push({
5386
5416
  type: "function_call_output",
5387
5417
  call_id: entry.call.call_id,
5388
- output: mergeToolOutput(outputPayload)
5418
+ output: toOpenAiToolOutput(outputPayload)
5389
5419
  });
5390
5420
  }
5391
5421
  }
@@ -5600,7 +5630,7 @@ async function runToolLoop(request) {
5600
5630
  toolOutputs.push({
5601
5631
  type: "custom_tool_call_output",
5602
5632
  call_id: entry.ids.callId,
5603
- output: mergeToolOutput(outputPayload)
5633
+ output: toOpenAiToolOutput(outputPayload)
5604
5634
  });
5605
5635
  } else {
5606
5636
  toolOutputs.push({
@@ -5614,7 +5644,7 @@ async function runToolLoop(request) {
5614
5644
  toolOutputs.push({
5615
5645
  type: "function_call_output",
5616
5646
  call_id: entry.ids.callId,
5617
- output: mergeToolOutput(outputPayload)
5647
+ output: toOpenAiToolOutput(outputPayload)
5618
5648
  });
5619
5649
  }
5620
5650
  }
@@ -7245,6 +7275,7 @@ function sleep2(ms) {
7245
7275
 
7246
7276
  // src/tools/filesystemTools.ts
7247
7277
  var import_node_path5 = __toESM(require("path"), 1);
7278
+ var import_node_buffer3 = require("buffer");
7248
7279
  var import_zod6 = require("zod");
7249
7280
 
7250
7281
  // src/tools/applyPatch.ts
@@ -7278,6 +7309,10 @@ var InMemoryAgentFilesystem = class {
7278
7309
  }
7279
7310
  return file.content;
7280
7311
  }
7312
+ async readBinaryFile(filePath) {
7313
+ const content = await this.readTextFile(filePath);
7314
+ return Buffer.from(content, "utf8");
7315
+ }
7281
7316
  async writeTextFile(filePath, content) {
7282
7317
  const absolutePath = import_node_path3.default.resolve(filePath);
7283
7318
  const parentPath = import_node_path3.default.dirname(absolutePath);
@@ -7390,6 +7425,7 @@ var InMemoryAgentFilesystem = class {
7390
7425
  function createNodeAgentFilesystem() {
7391
7426
  return {
7392
7427
  readTextFile: async (filePath) => import_node_fs3.promises.readFile(filePath, "utf8"),
7428
+ readBinaryFile: async (filePath) => import_node_fs3.promises.readFile(filePath),
7393
7429
  writeTextFile: async (filePath, content) => import_node_fs3.promises.writeFile(filePath, content, "utf8"),
7394
7430
  deleteFile: async (filePath) => import_node_fs3.promises.unlink(filePath),
7395
7431
  ensureDir: async (directoryPath) => {
@@ -7945,29 +7981,66 @@ function formatSummary(added, modified, deleted) {
7945
7981
 
7946
7982
  // src/tools/filesystemTools.ts
7947
7983
  var DEFAULT_READ_FILE_LINE_LIMIT = 2e3;
7948
- var DEFAULT_READ_FILES_LINE_LIMIT = 200;
7949
- var DEFAULT_READ_FILES_CHAR_LIMIT = 4e3;
7950
7984
  var DEFAULT_LIST_DIR_LIMIT = 25;
7951
7985
  var DEFAULT_LIST_DIR_DEPTH = 2;
7952
7986
  var DEFAULT_GREP_LIMIT = 100;
7953
7987
  var MAX_GREP_LIMIT = 2e3;
7988
+ var MAX_VIEW_IMAGE_BYTES = 10 * 1024 * 1024;
7954
7989
  var DEFAULT_MAX_LINE_LENGTH = 500;
7955
7990
  var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
7956
- var DEFAULT_TAB_WIDTH = 4;
7991
+ var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
7992
+ var IMAGE_MIME_BY_EXTENSION = {
7993
+ ".png": "image/png",
7994
+ ".jpg": "image/jpeg",
7995
+ ".jpeg": "image/jpeg",
7996
+ ".webp": "image/webp",
7997
+ ".gif": "image/gif"
7998
+ };
7999
+ function parseOptionalString(value) {
8000
+ if (value === null || value === void 0) {
8001
+ return void 0;
8002
+ }
8003
+ if (typeof value !== "string") {
8004
+ return void 0;
8005
+ }
8006
+ const trimmed = value.trim();
8007
+ if (trimmed.length === 0) {
8008
+ return void 0;
8009
+ }
8010
+ return trimmed;
8011
+ }
7957
8012
  var codexReadFileInputSchema = import_zod6.z.object({
7958
- file_path: import_zod6.z.string().min(1).describe(
7959
- "Path to the file (relative to cwd, or absolute. In sandbox mode, / maps to the sandbox root)."
8013
+ file_path: import_zod6.z.preprocess(
8014
+ (value) => parseOptionalString(value),
8015
+ import_zod6.z.string().min(1).optional().describe(
8016
+ "Path to the file (relative to cwd, or absolute. In sandbox mode, / maps to the sandbox root)."
8017
+ )
8018
+ ),
8019
+ path: import_zod6.z.preprocess(
8020
+ (value) => parseOptionalString(value),
8021
+ import_zod6.z.string().min(1).optional().describe(
8022
+ "Alias for file_path. If both file_path and path are provided they must be identical."
8023
+ )
7960
8024
  ),
7961
8025
  offset: import_zod6.z.number().int().min(1).nullish().describe("The line number to start reading from. Must be 1 or greater."),
7962
- limit: import_zod6.z.number().int().min(1).nullish().describe("The maximum number of lines to return."),
7963
- mode: import_zod6.z.enum(["slice", "indentation"]).nullish().describe('Optional mode selector: "slice" (default) or "indentation".'),
7964
- indentation: import_zod6.z.object({
7965
- anchor_line: import_zod6.z.number().int().min(1).nullish(),
7966
- max_levels: import_zod6.z.number().int().min(0).nullish(),
7967
- include_siblings: import_zod6.z.boolean().nullish(),
7968
- include_header: import_zod6.z.boolean().nullish(),
7969
- max_lines: import_zod6.z.number().int().min(1).nullish()
7970
- }).nullish()
8026
+ limit: import_zod6.z.number().int().min(1).nullish().describe("The maximum number of lines to return.")
8027
+ }).strict().superRefine((value, context) => {
8028
+ const filePath = value.file_path?.trim() ?? "";
8029
+ const aliasPath = value.path?.trim() ?? "";
8030
+ if (filePath.length === 0 && aliasPath.length === 0) {
8031
+ context.addIssue({
8032
+ code: import_zod6.z.ZodIssueCode.custom,
8033
+ message: "read_file requires file_path (or path alias).",
8034
+ path: ["file_path"]
8035
+ });
8036
+ }
8037
+ if (filePath.length > 0 && aliasPath.length > 0 && filePath !== aliasPath) {
8038
+ context.addIssue({
8039
+ code: import_zod6.z.ZodIssueCode.custom,
8040
+ message: "file_path and path must match when both are provided.",
8041
+ path: ["path"]
8042
+ });
8043
+ }
7971
8044
  });
7972
8045
  var codexListDirInputSchema = import_zod6.z.object({
7973
8046
  dir_path: import_zod6.z.string().min(1).describe(
@@ -7983,6 +8056,9 @@ var codexGrepFilesInputSchema = import_zod6.z.object({
7983
8056
  path: import_zod6.z.string().nullish().describe("Directory or file path to search. Defaults to cwd."),
7984
8057
  limit: import_zod6.z.number().int().min(1).nullish().describe("Maximum number of file paths to return (defaults to 100).")
7985
8058
  });
8059
+ var codexViewImageInputSchema = import_zod6.z.object({
8060
+ path: import_zod6.z.string().min(1).describe("Local filesystem path to an image file")
8061
+ });
7986
8062
  var applyPatchInputSchema = import_zod6.z.object({
7987
8063
  input: import_zod6.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
7988
8064
  });
@@ -7990,24 +8066,7 @@ var geminiReadFileInputSchema = import_zod6.z.object({
7990
8066
  file_path: import_zod6.z.string().min(1),
7991
8067
  offset: import_zod6.z.number().int().min(0).nullish(),
7992
8068
  limit: import_zod6.z.number().int().min(1).nullish()
7993
- });
7994
- var geminiReadFilesInputSchema = import_zod6.z.object({
7995
- paths: import_zod6.z.array(import_zod6.z.string().min(1)).min(1),
7996
- line_offset: import_zod6.z.number().int().min(0).nullish(),
7997
- line_limit: import_zod6.z.number().int().min(1).nullish(),
7998
- char_offset: import_zod6.z.number().int().min(0).nullish(),
7999
- char_limit: import_zod6.z.number().int().min(1).nullish(),
8000
- include_line_numbers: import_zod6.z.boolean().nullish()
8001
- }).superRefine((value, context) => {
8002
- const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
8003
- const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
8004
- if (hasLineWindow && hasCharWindow) {
8005
- context.addIssue({
8006
- code: import_zod6.z.ZodIssueCode.custom,
8007
- message: "Use either line_* or char_* window arguments, not both."
8008
- });
8009
- }
8010
- });
8069
+ }).strict();
8011
8070
  var geminiWriteFileInputSchema = import_zod6.z.object({
8012
8071
  file_path: import_zod6.z.string().min(1),
8013
8072
  content: import_zod6.z.string()
@@ -8090,7 +8149,8 @@ function createCodexFilesystemToolSet(options = {}) {
8090
8149
  apply_patch: createCodexApplyPatchTool(options),
8091
8150
  read_file: createCodexReadFileTool(options),
8092
8151
  list_dir: createListDirTool(options),
8093
- grep_files: createGrepFilesTool(options)
8152
+ grep_files: createGrepFilesTool(options),
8153
+ view_image: createViewImageTool(options)
8094
8154
  };
8095
8155
  }
8096
8156
  function createGeminiFilesystemToolSet(options = {}) {
@@ -8139,7 +8199,7 @@ function createCodexApplyPatchTool(options = {}) {
8139
8199
  }
8140
8200
  function createCodexReadFileTool(options = {}) {
8141
8201
  return tool({
8142
- description: "Reads a local file with 1-indexed line numbers, supporting slice and indentation-aware block modes.",
8202
+ description: "Reads a local UTF-8 text file with 1-indexed line numbers.",
8143
8203
  inputSchema: codexReadFileInputSchema,
8144
8204
  execute: async (input) => readFileCodex(input, options)
8145
8205
  });
@@ -8158,6 +8218,13 @@ function createGrepFilesTool(options = {}) {
8158
8218
  execute: async (input) => grepFilesCodex(input, options)
8159
8219
  });
8160
8220
  }
8221
+ function createViewImageTool(options = {}) {
8222
+ return tool({
8223
+ description: "View a local image from the filesystem (only use if given a full filepath by the user, and the image isn't already attached to the thread context within <image ...> tags).",
8224
+ inputSchema: codexViewImageInputSchema,
8225
+ execute: async (input) => viewImageCodex(input, options)
8226
+ });
8227
+ }
8161
8228
  function createGeminiReadFileTool(options = {}) {
8162
8229
  return tool({
8163
8230
  description: "Reads and returns the content of a specified file. Supports optional 0-based line offset and line limit.",
@@ -8165,13 +8232,6 @@ function createGeminiReadFileTool(options = {}) {
8165
8232
  execute: async (input) => readFileGemini(input, options)
8166
8233
  });
8167
8234
  }
8168
- function createReadFilesTool(options = {}) {
8169
- return tool({
8170
- description: "Reads one or more files with optional line-based or character-based slicing, similar to a controlled head/tail view.",
8171
- inputSchema: geminiReadFilesInputSchema,
8172
- execute: async (input) => readFilesGemini(input, options)
8173
- });
8174
- }
8175
8235
  function createWriteFileTool(options = {}) {
8176
8236
  return tool({
8177
8237
  description: "Writes content to a specified file in the local filesystem.",
@@ -8214,53 +8274,61 @@ function createGlobTool(options = {}) {
8214
8274
  execute: async (input) => globFilesGemini(input, options)
8215
8275
  });
8216
8276
  }
8277
+ function resolveCodexReadFilePath(input) {
8278
+ const filePath = parseOptionalString(input.file_path);
8279
+ if (filePath) {
8280
+ return filePath;
8281
+ }
8282
+ const aliasPath = parseOptionalString(input.path);
8283
+ if (aliasPath) {
8284
+ return aliasPath;
8285
+ }
8286
+ throw new Error("read_file requires file_path");
8287
+ }
8217
8288
  async function readFileCodex(input, options) {
8218
8289
  const runtime = resolveRuntime(options);
8219
- const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
8290
+ const filePath = resolvePathWithPolicy(
8291
+ resolveCodexReadFilePath(input),
8292
+ runtime.cwd,
8293
+ runtime.allowOutsideCwd
8294
+ );
8220
8295
  await runAccessHook2(runtime, {
8221
8296
  cwd: runtime.cwd,
8222
8297
  tool: "read_file",
8223
8298
  action: "read",
8224
8299
  path: filePath
8225
8300
  });
8226
- const content = await runtime.filesystem.readTextFile(filePath);
8301
+ const fileBytes = await readBinaryFile(runtime.filesystem, filePath);
8302
+ const imageMimeType = detectImageMimeType(fileBytes, filePath);
8303
+ if (imageMimeType) {
8304
+ throw new Error(
8305
+ `read_file only supports text files; "${toDisplayPath2(filePath, runtime.cwd)}" is an image (${imageMimeType}). Use view_image instead.`
8306
+ );
8307
+ }
8308
+ if (isPdfFile(fileBytes, filePath)) {
8309
+ throw new Error(
8310
+ `read_file only supports text files; "${toDisplayPath2(filePath, runtime.cwd)}" is a PDF.`
8311
+ );
8312
+ }
8313
+ if (!isValidUtf8(fileBytes)) {
8314
+ throw new Error(
8315
+ `read_file only supports UTF-8 text files; "${toDisplayPath2(filePath, runtime.cwd)}" appears to be binary.`
8316
+ );
8317
+ }
8318
+ const content = fileBytes.toString("utf8");
8227
8319
  const lines = splitLines(content);
8228
8320
  const offset = input.offset ?? 1;
8229
8321
  const limit = input.limit ?? DEFAULT_READ_FILE_LINE_LIMIT;
8230
- const mode = input.mode ?? "slice";
8231
8322
  if (offset > lines.length) {
8232
8323
  throw new Error("offset exceeds file length");
8233
8324
  }
8234
- if (mode === "slice") {
8235
- const output = [];
8236
- const lastLine = Math.min(lines.length, offset + limit - 1);
8237
- for (let lineNumber = offset; lineNumber <= lastLine; lineNumber += 1) {
8238
- const line = lines[lineNumber - 1] ?? "";
8239
- output.push(`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`);
8240
- }
8241
- return output.join("\n");
8242
- }
8243
- const indentation = input.indentation ?? {};
8244
- const anchorLine = indentation.anchor_line ?? offset;
8245
- if (anchorLine < 1 || anchorLine > lines.length) {
8246
- throw new Error("anchor_line exceeds file length");
8247
- }
8248
- const records = lines.map((line, index) => ({
8249
- number: index + 1,
8250
- raw: line,
8251
- display: truncateAtCodePointBoundary(line, runtime.maxLineLength),
8252
- indent: measureIndent(line, DEFAULT_TAB_WIDTH)
8253
- }));
8254
- const selected = readWithIndentationMode({
8255
- records,
8256
- anchorLine,
8257
- limit,
8258
- maxLevels: indentation.max_levels ?? 0,
8259
- includeSiblings: indentation.include_siblings ?? false,
8260
- includeHeader: indentation.include_header ?? true,
8261
- maxLines: indentation.max_lines ?? void 0
8262
- });
8263
- return selected.map((record) => `L${record.number}: ${record.display}`).join("\n");
8325
+ const output = [];
8326
+ const lastLine = Math.min(lines.length, offset + limit - 1);
8327
+ for (let lineNumber = offset; lineNumber <= lastLine; lineNumber += 1) {
8328
+ const line = lines[lineNumber - 1] ?? "";
8329
+ output.push(`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`);
8330
+ }
8331
+ return output.join("\n");
8264
8332
  }
8265
8333
  async function listDirectoryCodex(input, options) {
8266
8334
  const runtime = resolveRuntime(options);
@@ -8348,6 +8416,85 @@ async function grepFilesCodex(input, options) {
8348
8416
  const limit = Math.min(input.limit ?? DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
8349
8417
  return matches.slice(0, limit).map((match) => match.filePath).join("\n");
8350
8418
  }
8419
+ async function viewImageCodex(input, options) {
8420
+ const runtime = resolveRuntime(options);
8421
+ const imagePath = resolvePathWithPolicy(input.path, runtime.cwd, runtime.allowOutsideCwd);
8422
+ await runAccessHook2(runtime, {
8423
+ cwd: runtime.cwd,
8424
+ tool: "view_image",
8425
+ action: "read",
8426
+ path: imagePath
8427
+ });
8428
+ const stats = await runtime.filesystem.stat(imagePath);
8429
+ if (stats.kind !== "file") {
8430
+ throw new Error(`image path \`${toDisplayPath2(imagePath, runtime.cwd)}\` is not a file`);
8431
+ }
8432
+ const bytes = await readBinaryFile(runtime.filesystem, imagePath);
8433
+ if (bytes.byteLength > MAX_VIEW_IMAGE_BYTES) {
8434
+ return [
8435
+ {
8436
+ type: "input_text",
8437
+ text: `Codex cannot attach image at \`${toDisplayPath2(imagePath, runtime.cwd)}\`: image exceeds ${MAX_VIEW_IMAGE_BYTES} bytes.`
8438
+ }
8439
+ ];
8440
+ }
8441
+ const mimeType = detectImageMimeType(bytes, imagePath);
8442
+ if (!mimeType) {
8443
+ return [
8444
+ {
8445
+ type: "input_text",
8446
+ text: `Codex cannot attach image at \`${toDisplayPath2(imagePath, runtime.cwd)}\`: unsupported image format.`
8447
+ }
8448
+ ];
8449
+ }
8450
+ return [
8451
+ {
8452
+ type: "input_image",
8453
+ image_url: `data:${mimeType};base64,${bytes.toString("base64")}`
8454
+ }
8455
+ ];
8456
+ }
8457
+ async function readBinaryFile(filesystem, filePath) {
8458
+ if (typeof filesystem.readBinaryFile === "function") {
8459
+ return await filesystem.readBinaryFile(filePath);
8460
+ }
8461
+ const text = await filesystem.readTextFile(filePath);
8462
+ return import_node_buffer3.Buffer.from(text, "utf8");
8463
+ }
8464
+ function detectImageMimeType(buffer, filePath) {
8465
+ if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10) {
8466
+ return "image/png";
8467
+ }
8468
+ if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
8469
+ return "image/jpeg";
8470
+ }
8471
+ if (buffer.length >= 6) {
8472
+ const signature = buffer.subarray(0, 6).toString("ascii");
8473
+ if (signature === "GIF87a" || signature === "GIF89a") {
8474
+ return "image/gif";
8475
+ }
8476
+ }
8477
+ if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
8478
+ return "image/webp";
8479
+ }
8480
+ const fromExtension = IMAGE_MIME_BY_EXTENSION[import_node_path5.default.extname(filePath).toLowerCase()];
8481
+ if (fromExtension && SUPPORTED_IMAGE_MIME_TYPES.has(fromExtension)) {
8482
+ return fromExtension;
8483
+ }
8484
+ return void 0;
8485
+ }
8486
+ function isPdfFile(buffer, filePath) {
8487
+ if (buffer.length >= 5 && buffer.subarray(0, 5).toString("ascii") === "%PDF-") {
8488
+ return true;
8489
+ }
8490
+ return import_node_path5.default.extname(filePath).toLowerCase() === ".pdf";
8491
+ }
8492
+ function isValidUtf8(buffer) {
8493
+ if (buffer.length === 0) {
8494
+ return true;
8495
+ }
8496
+ return import_node_buffer3.Buffer.from(buffer.toString("utf8"), "utf8").equals(buffer);
8497
+ }
8351
8498
  async function readFileGemini(input, options) {
8352
8499
  const runtime = resolveRuntime(options);
8353
8500
  const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
@@ -8369,56 +8516,6 @@ async function readFileGemini(input, options) {
8369
8516
  (line, index) => `L${offset + index + 1}: ${truncateAtCodePointBoundary(line ?? "", runtime.maxLineLength)}`
8370
8517
  ).join("\n");
8371
8518
  }
8372
- async function readFilesGemini(input, options) {
8373
- const runtime = resolveRuntime(options);
8374
- const useCharWindow = input.char_offset !== void 0 || input.char_limit !== void 0;
8375
- const lineOffset = Math.max(0, input.line_offset ?? 0);
8376
- const lineLimit = input.line_limit ?? DEFAULT_READ_FILES_LINE_LIMIT;
8377
- const charOffset = Math.max(0, input.char_offset ?? 0);
8378
- const charLimit = input.char_limit ?? DEFAULT_READ_FILES_CHAR_LIMIT;
8379
- const includeLineNumbers = input.include_line_numbers !== false;
8380
- const sections = [];
8381
- for (const rawPath of input.paths) {
8382
- const filePath = resolvePathWithPolicy(rawPath, runtime.cwd, runtime.allowOutsideCwd);
8383
- await runAccessHook2(runtime, {
8384
- cwd: runtime.cwd,
8385
- tool: "read_files",
8386
- action: "read",
8387
- path: filePath
8388
- });
8389
- const content = await runtime.filesystem.readTextFile(filePath);
8390
- const displayPath = normalizeSlashes(toDisplayPath2(filePath, runtime.cwd));
8391
- sections.push(`==> ${displayPath} <==`);
8392
- if (useCharWindow) {
8393
- if (charOffset >= content.length) {
8394
- sections.push("");
8395
- continue;
8396
- }
8397
- const end2 = Math.min(content.length, charOffset + charLimit);
8398
- sections.push(content.slice(charOffset, end2));
8399
- continue;
8400
- }
8401
- const lines = splitLines(content);
8402
- if (lineOffset >= lines.length) {
8403
- sections.push("");
8404
- continue;
8405
- }
8406
- const end = Math.min(lines.length, lineOffset + lineLimit);
8407
- const selected = lines.slice(lineOffset, end);
8408
- if (includeLineNumbers) {
8409
- for (let index = 0; index < selected.length; index += 1) {
8410
- const lineNumber = lineOffset + index + 1;
8411
- const line = selected[index] ?? "";
8412
- sections.push(
8413
- `L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`
8414
- );
8415
- }
8416
- continue;
8417
- }
8418
- sections.push(selected.join("\n"));
8419
- }
8420
- return sections.join("\n");
8421
- }
8422
8519
  async function writeFileGemini(input, options) {
8423
8520
  const runtime = resolveRuntime(options);
8424
8521
  const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
@@ -8727,117 +8824,6 @@ function truncateAtCodePointBoundary(value, maxLength) {
8727
8824
  }
8728
8825
  return Array.from(value).slice(0, maxLength).join("");
8729
8826
  }
8730
- function measureIndent(line, tabWidth) {
8731
- let count = 0;
8732
- for (const char of line) {
8733
- if (char === " ") {
8734
- count += 1;
8735
- continue;
8736
- }
8737
- if (char === " ") {
8738
- count += tabWidth;
8739
- continue;
8740
- }
8741
- break;
8742
- }
8743
- return count;
8744
- }
8745
- function computeEffectiveIndents(records) {
8746
- const effective = [];
8747
- let previous = 0;
8748
- for (const record of records) {
8749
- if (record.raw.trim().length === 0) {
8750
- effective.push(previous);
8751
- } else {
8752
- previous = record.indent;
8753
- effective.push(previous);
8754
- }
8755
- }
8756
- return effective;
8757
- }
8758
- function trimBoundaryBlankLines(records) {
8759
- while (records.length > 0 && records[0]?.raw.trim().length === 0) {
8760
- records.shift();
8761
- }
8762
- while (records.length > 0 && records[records.length - 1]?.raw.trim().length === 0) {
8763
- records.pop();
8764
- }
8765
- }
8766
- function isCommentLine(line) {
8767
- const trimmed = line.trim();
8768
- return trimmed.startsWith("#") || trimmed.startsWith("//") || trimmed.startsWith("--");
8769
- }
8770
- function readWithIndentationMode(params) {
8771
- const { records, anchorLine, limit, maxLevels, includeSiblings, includeHeader, maxLines } = params;
8772
- const anchorIndex = anchorLine - 1;
8773
- const effectiveIndents = computeEffectiveIndents(records);
8774
- const anchorIndent = effectiveIndents[anchorIndex] ?? 0;
8775
- const minIndent = maxLevels === 0 ? 0 : Math.max(anchorIndent - maxLevels * DEFAULT_TAB_WIDTH, 0);
8776
- const guardLimit = maxLines ?? limit;
8777
- const finalLimit = Math.min(limit, guardLimit, records.length);
8778
- if (finalLimit <= 1) {
8779
- return [records[anchorIndex]].filter((entry) => Boolean(entry));
8780
- }
8781
- let upper = anchorIndex - 1;
8782
- let lower = anchorIndex + 1;
8783
- let upperMinIndentHits = 0;
8784
- let lowerMinIndentHits = 0;
8785
- const output = [records[anchorIndex]].filter(
8786
- (entry) => Boolean(entry)
8787
- );
8788
- while (output.length < finalLimit) {
8789
- let progressed = 0;
8790
- if (upper >= 0) {
8791
- const candidate = records[upper];
8792
- const candidateIndent = effectiveIndents[upper] ?? 0;
8793
- if (candidate && candidateIndent >= minIndent) {
8794
- output.unshift(candidate);
8795
- progressed += 1;
8796
- upper -= 1;
8797
- if (candidateIndent === minIndent && !includeSiblings) {
8798
- const allowHeaderComment = includeHeader && isCommentLine(candidate.raw);
8799
- const canTakeLine = allowHeaderComment || upperMinIndentHits === 0;
8800
- if (canTakeLine) {
8801
- upperMinIndentHits += 1;
8802
- } else {
8803
- output.shift();
8804
- progressed -= 1;
8805
- upper = -1;
8806
- }
8807
- }
8808
- if (output.length >= finalLimit) {
8809
- break;
8810
- }
8811
- } else {
8812
- upper = -1;
8813
- }
8814
- }
8815
- if (lower < records.length) {
8816
- const candidate = records[lower];
8817
- const candidateIndent = effectiveIndents[lower] ?? 0;
8818
- if (candidate && candidateIndent >= minIndent) {
8819
- output.push(candidate);
8820
- progressed += 1;
8821
- lower += 1;
8822
- if (candidateIndent === minIndent && !includeSiblings) {
8823
- if (lowerMinIndentHits > 0) {
8824
- output.pop();
8825
- progressed -= 1;
8826
- lower = records.length;
8827
- }
8828
- lowerMinIndentHits += 1;
8829
- }
8830
- } else {
8831
- lower = records.length;
8832
- }
8833
- }
8834
- if (progressed === 0) {
8835
- break;
8836
- }
8837
- }
8838
- trimBoundaryBlankLines(output);
8839
- return output;
8840
- }
8841
8827
  async function collectDirectoryEntries(filesystem, rootPath, depth, maxLineLength) {
8842
8828
  const queue = [
8843
8829
  { path: rootPath, relativePrefix: "", remainingDepth: depth }
@@ -10156,10 +10142,10 @@ async function runCandidateEvolution(options) {
10156
10142
  createListDirectoryTool,
10157
10143
  createModelAgnosticFilesystemToolSet,
10158
10144
  createNodeAgentFilesystem,
10159
- createReadFilesTool,
10160
10145
  createReplaceTool,
10161
10146
  createRgSearchTool,
10162
10147
  createToolLoopSteeringChannel,
10148
+ createViewImageTool,
10163
10149
  createWriteFileTool,
10164
10150
  customTool,
10165
10151
  encodeChatGptAuthJson,