@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 +229 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -32
- package/dist/index.d.ts +33 -32
- package/dist/index.js +228 -242
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -2105,7 +2105,6 @@ function getGoogleAuthOptions(scopes) {
|
|
|
2105
2105
|
|
|
2106
2106
|
// src/google/client.ts
|
|
2107
2107
|
var GEMINI_TEXT_MODEL_IDS = [
|
|
2108
|
-
"gemini-3-pro-preview",
|
|
2109
2108
|
"gemini-3.1-pro-preview",
|
|
2110
2109
|
"gemini-3-flash-preview",
|
|
2111
2110
|
"gemini-2.5-pro",
|
|
@@ -3909,6 +3908,38 @@ function mergeToolOutput(value) {
|
|
|
3909
3908
|
return JSON.stringify({ error: "Failed to serialize tool output", detail: message });
|
|
3910
3909
|
}
|
|
3911
3910
|
}
|
|
3911
|
+
function isLlmToolOutputContentItem(value) {
|
|
3912
|
+
if (!isPlainRecord(value)) {
|
|
3913
|
+
return false;
|
|
3914
|
+
}
|
|
3915
|
+
const itemType = typeof value.type === "string" ? value.type : "";
|
|
3916
|
+
if (itemType === "input_text") {
|
|
3917
|
+
return typeof value.text === "string";
|
|
3918
|
+
}
|
|
3919
|
+
if (itemType === "input_image") {
|
|
3920
|
+
return typeof value.image_url === "string";
|
|
3921
|
+
}
|
|
3922
|
+
if (itemType === "input_file") {
|
|
3923
|
+
const keys = ["file_data", "file_id", "file_url", "filename"];
|
|
3924
|
+
for (const key of keys) {
|
|
3925
|
+
const part = value[key];
|
|
3926
|
+
if (part !== void 0 && part !== null && typeof part !== "string") {
|
|
3927
|
+
return false;
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
return true;
|
|
3931
|
+
}
|
|
3932
|
+
return false;
|
|
3933
|
+
}
|
|
3934
|
+
function toOpenAiToolOutput(value) {
|
|
3935
|
+
if (isLlmToolOutputContentItem(value)) {
|
|
3936
|
+
return [value];
|
|
3937
|
+
}
|
|
3938
|
+
if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
|
|
3939
|
+
return value;
|
|
3940
|
+
}
|
|
3941
|
+
return mergeToolOutput(value);
|
|
3942
|
+
}
|
|
3912
3943
|
function parseOpenAiToolArguments(raw) {
|
|
3913
3944
|
const trimmed = raw.trim();
|
|
3914
3945
|
if (trimmed.length === 0) {
|
|
@@ -4264,7 +4295,6 @@ function resolveGeminiThinkingConfig(modelId) {
|
|
|
4264
4295
|
return void 0;
|
|
4265
4296
|
}
|
|
4266
4297
|
switch (modelId) {
|
|
4267
|
-
case "gemini-3-pro-preview":
|
|
4268
4298
|
case "gemini-3.1-pro-preview":
|
|
4269
4299
|
return { includeThoughts: true };
|
|
4270
4300
|
case "gemini-3-flash-preview":
|
|
@@ -5266,13 +5296,13 @@ async function runToolLoop(request) {
|
|
|
5266
5296
|
toolOutputs.push({
|
|
5267
5297
|
type: "custom_tool_call_output",
|
|
5268
5298
|
call_id: entry.call.call_id,
|
|
5269
|
-
output:
|
|
5299
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
5270
5300
|
});
|
|
5271
5301
|
} else {
|
|
5272
5302
|
toolOutputs.push({
|
|
5273
5303
|
type: "function_call_output",
|
|
5274
5304
|
call_id: entry.call.call_id,
|
|
5275
|
-
output:
|
|
5305
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
5276
5306
|
});
|
|
5277
5307
|
}
|
|
5278
5308
|
}
|
|
@@ -5487,7 +5517,7 @@ async function runToolLoop(request) {
|
|
|
5487
5517
|
toolOutputs.push({
|
|
5488
5518
|
type: "custom_tool_call_output",
|
|
5489
5519
|
call_id: entry.ids.callId,
|
|
5490
|
-
output:
|
|
5520
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
5491
5521
|
});
|
|
5492
5522
|
} else {
|
|
5493
5523
|
toolOutputs.push({
|
|
@@ -5501,7 +5531,7 @@ async function runToolLoop(request) {
|
|
|
5501
5531
|
toolOutputs.push({
|
|
5502
5532
|
type: "function_call_output",
|
|
5503
5533
|
call_id: entry.ids.callId,
|
|
5504
|
-
output:
|
|
5534
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
5505
5535
|
});
|
|
5506
5536
|
}
|
|
5507
5537
|
}
|
|
@@ -7132,6 +7162,7 @@ function sleep2(ms) {
|
|
|
7132
7162
|
|
|
7133
7163
|
// src/tools/filesystemTools.ts
|
|
7134
7164
|
import path5 from "path";
|
|
7165
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
7135
7166
|
import { z as z6 } from "zod";
|
|
7136
7167
|
|
|
7137
7168
|
// src/tools/applyPatch.ts
|
|
@@ -7165,6 +7196,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
7165
7196
|
}
|
|
7166
7197
|
return file.content;
|
|
7167
7198
|
}
|
|
7199
|
+
async readBinaryFile(filePath) {
|
|
7200
|
+
const content = await this.readTextFile(filePath);
|
|
7201
|
+
return Buffer.from(content, "utf8");
|
|
7202
|
+
}
|
|
7168
7203
|
async writeTextFile(filePath, content) {
|
|
7169
7204
|
const absolutePath = path3.resolve(filePath);
|
|
7170
7205
|
const parentPath = path3.dirname(absolutePath);
|
|
@@ -7277,6 +7312,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
7277
7312
|
function createNodeAgentFilesystem() {
|
|
7278
7313
|
return {
|
|
7279
7314
|
readTextFile: async (filePath) => fs3.readFile(filePath, "utf8"),
|
|
7315
|
+
readBinaryFile: async (filePath) => fs3.readFile(filePath),
|
|
7280
7316
|
writeTextFile: async (filePath, content) => fs3.writeFile(filePath, content, "utf8"),
|
|
7281
7317
|
deleteFile: async (filePath) => fs3.unlink(filePath),
|
|
7282
7318
|
ensureDir: async (directoryPath) => {
|
|
@@ -7832,29 +7868,66 @@ function formatSummary(added, modified, deleted) {
|
|
|
7832
7868
|
|
|
7833
7869
|
// src/tools/filesystemTools.ts
|
|
7834
7870
|
var DEFAULT_READ_FILE_LINE_LIMIT = 2e3;
|
|
7835
|
-
var DEFAULT_READ_FILES_LINE_LIMIT = 200;
|
|
7836
|
-
var DEFAULT_READ_FILES_CHAR_LIMIT = 4e3;
|
|
7837
7871
|
var DEFAULT_LIST_DIR_LIMIT = 25;
|
|
7838
7872
|
var DEFAULT_LIST_DIR_DEPTH = 2;
|
|
7839
7873
|
var DEFAULT_GREP_LIMIT = 100;
|
|
7840
7874
|
var MAX_GREP_LIMIT = 2e3;
|
|
7875
|
+
var MAX_VIEW_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
7841
7876
|
var DEFAULT_MAX_LINE_LENGTH = 500;
|
|
7842
7877
|
var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
|
|
7843
|
-
var
|
|
7878
|
+
var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
7879
|
+
var IMAGE_MIME_BY_EXTENSION = {
|
|
7880
|
+
".png": "image/png",
|
|
7881
|
+
".jpg": "image/jpeg",
|
|
7882
|
+
".jpeg": "image/jpeg",
|
|
7883
|
+
".webp": "image/webp",
|
|
7884
|
+
".gif": "image/gif"
|
|
7885
|
+
};
|
|
7886
|
+
function parseOptionalString(value) {
|
|
7887
|
+
if (value === null || value === void 0) {
|
|
7888
|
+
return void 0;
|
|
7889
|
+
}
|
|
7890
|
+
if (typeof value !== "string") {
|
|
7891
|
+
return void 0;
|
|
7892
|
+
}
|
|
7893
|
+
const trimmed = value.trim();
|
|
7894
|
+
if (trimmed.length === 0) {
|
|
7895
|
+
return void 0;
|
|
7896
|
+
}
|
|
7897
|
+
return trimmed;
|
|
7898
|
+
}
|
|
7844
7899
|
var codexReadFileInputSchema = z6.object({
|
|
7845
|
-
file_path: z6.
|
|
7846
|
-
|
|
7900
|
+
file_path: z6.preprocess(
|
|
7901
|
+
(value) => parseOptionalString(value),
|
|
7902
|
+
z6.string().min(1).optional().describe(
|
|
7903
|
+
"Path to the file (relative to cwd, or absolute. In sandbox mode, / maps to the sandbox root)."
|
|
7904
|
+
)
|
|
7905
|
+
),
|
|
7906
|
+
path: z6.preprocess(
|
|
7907
|
+
(value) => parseOptionalString(value),
|
|
7908
|
+
z6.string().min(1).optional().describe(
|
|
7909
|
+
"Alias for file_path. If both file_path and path are provided they must be identical."
|
|
7910
|
+
)
|
|
7847
7911
|
),
|
|
7848
7912
|
offset: z6.number().int().min(1).nullish().describe("The line number to start reading from. Must be 1 or greater."),
|
|
7849
|
-
limit: z6.number().int().min(1).nullish().describe("The maximum number of lines to return.")
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7913
|
+
limit: z6.number().int().min(1).nullish().describe("The maximum number of lines to return.")
|
|
7914
|
+
}).strict().superRefine((value, context) => {
|
|
7915
|
+
const filePath = value.file_path?.trim() ?? "";
|
|
7916
|
+
const aliasPath = value.path?.trim() ?? "";
|
|
7917
|
+
if (filePath.length === 0 && aliasPath.length === 0) {
|
|
7918
|
+
context.addIssue({
|
|
7919
|
+
code: z6.ZodIssueCode.custom,
|
|
7920
|
+
message: "read_file requires file_path (or path alias).",
|
|
7921
|
+
path: ["file_path"]
|
|
7922
|
+
});
|
|
7923
|
+
}
|
|
7924
|
+
if (filePath.length > 0 && aliasPath.length > 0 && filePath !== aliasPath) {
|
|
7925
|
+
context.addIssue({
|
|
7926
|
+
code: z6.ZodIssueCode.custom,
|
|
7927
|
+
message: "file_path and path must match when both are provided.",
|
|
7928
|
+
path: ["path"]
|
|
7929
|
+
});
|
|
7930
|
+
}
|
|
7858
7931
|
});
|
|
7859
7932
|
var codexListDirInputSchema = z6.object({
|
|
7860
7933
|
dir_path: z6.string().min(1).describe(
|
|
@@ -7870,6 +7943,9 @@ var codexGrepFilesInputSchema = z6.object({
|
|
|
7870
7943
|
path: z6.string().nullish().describe("Directory or file path to search. Defaults to cwd."),
|
|
7871
7944
|
limit: z6.number().int().min(1).nullish().describe("Maximum number of file paths to return (defaults to 100).")
|
|
7872
7945
|
});
|
|
7946
|
+
var codexViewImageInputSchema = z6.object({
|
|
7947
|
+
path: z6.string().min(1).describe("Local filesystem path to an image file")
|
|
7948
|
+
});
|
|
7873
7949
|
var applyPatchInputSchema = z6.object({
|
|
7874
7950
|
input: z6.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
7875
7951
|
});
|
|
@@ -7877,24 +7953,7 @@ var geminiReadFileInputSchema = z6.object({
|
|
|
7877
7953
|
file_path: z6.string().min(1),
|
|
7878
7954
|
offset: z6.number().int().min(0).nullish(),
|
|
7879
7955
|
limit: z6.number().int().min(1).nullish()
|
|
7880
|
-
});
|
|
7881
|
-
var geminiReadFilesInputSchema = z6.object({
|
|
7882
|
-
paths: z6.array(z6.string().min(1)).min(1),
|
|
7883
|
-
line_offset: z6.number().int().min(0).nullish(),
|
|
7884
|
-
line_limit: z6.number().int().min(1).nullish(),
|
|
7885
|
-
char_offset: z6.number().int().min(0).nullish(),
|
|
7886
|
-
char_limit: z6.number().int().min(1).nullish(),
|
|
7887
|
-
include_line_numbers: z6.boolean().nullish()
|
|
7888
|
-
}).superRefine((value, context) => {
|
|
7889
|
-
const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
|
|
7890
|
-
const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
|
|
7891
|
-
if (hasLineWindow && hasCharWindow) {
|
|
7892
|
-
context.addIssue({
|
|
7893
|
-
code: z6.ZodIssueCode.custom,
|
|
7894
|
-
message: "Use either line_* or char_* window arguments, not both."
|
|
7895
|
-
});
|
|
7896
|
-
}
|
|
7897
|
-
});
|
|
7956
|
+
}).strict();
|
|
7898
7957
|
var geminiWriteFileInputSchema = z6.object({
|
|
7899
7958
|
file_path: z6.string().min(1),
|
|
7900
7959
|
content: z6.string()
|
|
@@ -7977,7 +8036,8 @@ function createCodexFilesystemToolSet(options = {}) {
|
|
|
7977
8036
|
apply_patch: createCodexApplyPatchTool(options),
|
|
7978
8037
|
read_file: createCodexReadFileTool(options),
|
|
7979
8038
|
list_dir: createListDirTool(options),
|
|
7980
|
-
grep_files: createGrepFilesTool(options)
|
|
8039
|
+
grep_files: createGrepFilesTool(options),
|
|
8040
|
+
view_image: createViewImageTool(options)
|
|
7981
8041
|
};
|
|
7982
8042
|
}
|
|
7983
8043
|
function createGeminiFilesystemToolSet(options = {}) {
|
|
@@ -8026,7 +8086,7 @@ function createCodexApplyPatchTool(options = {}) {
|
|
|
8026
8086
|
}
|
|
8027
8087
|
function createCodexReadFileTool(options = {}) {
|
|
8028
8088
|
return tool({
|
|
8029
|
-
description: "Reads a local file with 1-indexed line numbers
|
|
8089
|
+
description: "Reads a local UTF-8 text file with 1-indexed line numbers.",
|
|
8030
8090
|
inputSchema: codexReadFileInputSchema,
|
|
8031
8091
|
execute: async (input) => readFileCodex(input, options)
|
|
8032
8092
|
});
|
|
@@ -8045,6 +8105,13 @@ function createGrepFilesTool(options = {}) {
|
|
|
8045
8105
|
execute: async (input) => grepFilesCodex(input, options)
|
|
8046
8106
|
});
|
|
8047
8107
|
}
|
|
8108
|
+
function createViewImageTool(options = {}) {
|
|
8109
|
+
return tool({
|
|
8110
|
+
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).",
|
|
8111
|
+
inputSchema: codexViewImageInputSchema,
|
|
8112
|
+
execute: async (input) => viewImageCodex(input, options)
|
|
8113
|
+
});
|
|
8114
|
+
}
|
|
8048
8115
|
function createGeminiReadFileTool(options = {}) {
|
|
8049
8116
|
return tool({
|
|
8050
8117
|
description: "Reads and returns the content of a specified file. Supports optional 0-based line offset and line limit.",
|
|
@@ -8052,13 +8119,6 @@ function createGeminiReadFileTool(options = {}) {
|
|
|
8052
8119
|
execute: async (input) => readFileGemini(input, options)
|
|
8053
8120
|
});
|
|
8054
8121
|
}
|
|
8055
|
-
function createReadFilesTool(options = {}) {
|
|
8056
|
-
return tool({
|
|
8057
|
-
description: "Reads one or more files with optional line-based or character-based slicing, similar to a controlled head/tail view.",
|
|
8058
|
-
inputSchema: geminiReadFilesInputSchema,
|
|
8059
|
-
execute: async (input) => readFilesGemini(input, options)
|
|
8060
|
-
});
|
|
8061
|
-
}
|
|
8062
8122
|
function createWriteFileTool(options = {}) {
|
|
8063
8123
|
return tool({
|
|
8064
8124
|
description: "Writes content to a specified file in the local filesystem.",
|
|
@@ -8101,53 +8161,61 @@ function createGlobTool(options = {}) {
|
|
|
8101
8161
|
execute: async (input) => globFilesGemini(input, options)
|
|
8102
8162
|
});
|
|
8103
8163
|
}
|
|
8164
|
+
function resolveCodexReadFilePath(input) {
|
|
8165
|
+
const filePath = parseOptionalString(input.file_path);
|
|
8166
|
+
if (filePath) {
|
|
8167
|
+
return filePath;
|
|
8168
|
+
}
|
|
8169
|
+
const aliasPath = parseOptionalString(input.path);
|
|
8170
|
+
if (aliasPath) {
|
|
8171
|
+
return aliasPath;
|
|
8172
|
+
}
|
|
8173
|
+
throw new Error("read_file requires file_path");
|
|
8174
|
+
}
|
|
8104
8175
|
async function readFileCodex(input, options) {
|
|
8105
8176
|
const runtime = resolveRuntime(options);
|
|
8106
|
-
const filePath = resolvePathWithPolicy(
|
|
8177
|
+
const filePath = resolvePathWithPolicy(
|
|
8178
|
+
resolveCodexReadFilePath(input),
|
|
8179
|
+
runtime.cwd,
|
|
8180
|
+
runtime.allowOutsideCwd
|
|
8181
|
+
);
|
|
8107
8182
|
await runAccessHook2(runtime, {
|
|
8108
8183
|
cwd: runtime.cwd,
|
|
8109
8184
|
tool: "read_file",
|
|
8110
8185
|
action: "read",
|
|
8111
8186
|
path: filePath
|
|
8112
8187
|
});
|
|
8113
|
-
const
|
|
8188
|
+
const fileBytes = await readBinaryFile(runtime.filesystem, filePath);
|
|
8189
|
+
const imageMimeType = detectImageMimeType(fileBytes, filePath);
|
|
8190
|
+
if (imageMimeType) {
|
|
8191
|
+
throw new Error(
|
|
8192
|
+
`read_file only supports text files; "${toDisplayPath2(filePath, runtime.cwd)}" is an image (${imageMimeType}). Use view_image instead.`
|
|
8193
|
+
);
|
|
8194
|
+
}
|
|
8195
|
+
if (isPdfFile(fileBytes, filePath)) {
|
|
8196
|
+
throw new Error(
|
|
8197
|
+
`read_file only supports text files; "${toDisplayPath2(filePath, runtime.cwd)}" is a PDF.`
|
|
8198
|
+
);
|
|
8199
|
+
}
|
|
8200
|
+
if (!isValidUtf8(fileBytes)) {
|
|
8201
|
+
throw new Error(
|
|
8202
|
+
`read_file only supports UTF-8 text files; "${toDisplayPath2(filePath, runtime.cwd)}" appears to be binary.`
|
|
8203
|
+
);
|
|
8204
|
+
}
|
|
8205
|
+
const content = fileBytes.toString("utf8");
|
|
8114
8206
|
const lines = splitLines(content);
|
|
8115
8207
|
const offset = input.offset ?? 1;
|
|
8116
8208
|
const limit = input.limit ?? DEFAULT_READ_FILE_LINE_LIMIT;
|
|
8117
|
-
const mode = input.mode ?? "slice";
|
|
8118
8209
|
if (offset > lines.length) {
|
|
8119
8210
|
throw new Error("offset exceeds file length");
|
|
8120
8211
|
}
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
return output.join("\n");
|
|
8129
|
-
}
|
|
8130
|
-
const indentation = input.indentation ?? {};
|
|
8131
|
-
const anchorLine = indentation.anchor_line ?? offset;
|
|
8132
|
-
if (anchorLine < 1 || anchorLine > lines.length) {
|
|
8133
|
-
throw new Error("anchor_line exceeds file length");
|
|
8134
|
-
}
|
|
8135
|
-
const records = lines.map((line, index) => ({
|
|
8136
|
-
number: index + 1,
|
|
8137
|
-
raw: line,
|
|
8138
|
-
display: truncateAtCodePointBoundary(line, runtime.maxLineLength),
|
|
8139
|
-
indent: measureIndent(line, DEFAULT_TAB_WIDTH)
|
|
8140
|
-
}));
|
|
8141
|
-
const selected = readWithIndentationMode({
|
|
8142
|
-
records,
|
|
8143
|
-
anchorLine,
|
|
8144
|
-
limit,
|
|
8145
|
-
maxLevels: indentation.max_levels ?? 0,
|
|
8146
|
-
includeSiblings: indentation.include_siblings ?? false,
|
|
8147
|
-
includeHeader: indentation.include_header ?? true,
|
|
8148
|
-
maxLines: indentation.max_lines ?? void 0
|
|
8149
|
-
});
|
|
8150
|
-
return selected.map((record) => `L${record.number}: ${record.display}`).join("\n");
|
|
8212
|
+
const output = [];
|
|
8213
|
+
const lastLine = Math.min(lines.length, offset + limit - 1);
|
|
8214
|
+
for (let lineNumber = offset; lineNumber <= lastLine; lineNumber += 1) {
|
|
8215
|
+
const line = lines[lineNumber - 1] ?? "";
|
|
8216
|
+
output.push(`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`);
|
|
8217
|
+
}
|
|
8218
|
+
return output.join("\n");
|
|
8151
8219
|
}
|
|
8152
8220
|
async function listDirectoryCodex(input, options) {
|
|
8153
8221
|
const runtime = resolveRuntime(options);
|
|
@@ -8235,6 +8303,85 @@ async function grepFilesCodex(input, options) {
|
|
|
8235
8303
|
const limit = Math.min(input.limit ?? DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
|
|
8236
8304
|
return matches.slice(0, limit).map((match) => match.filePath).join("\n");
|
|
8237
8305
|
}
|
|
8306
|
+
async function viewImageCodex(input, options) {
|
|
8307
|
+
const runtime = resolveRuntime(options);
|
|
8308
|
+
const imagePath = resolvePathWithPolicy(input.path, runtime.cwd, runtime.allowOutsideCwd);
|
|
8309
|
+
await runAccessHook2(runtime, {
|
|
8310
|
+
cwd: runtime.cwd,
|
|
8311
|
+
tool: "view_image",
|
|
8312
|
+
action: "read",
|
|
8313
|
+
path: imagePath
|
|
8314
|
+
});
|
|
8315
|
+
const stats = await runtime.filesystem.stat(imagePath);
|
|
8316
|
+
if (stats.kind !== "file") {
|
|
8317
|
+
throw new Error(`image path \`${toDisplayPath2(imagePath, runtime.cwd)}\` is not a file`);
|
|
8318
|
+
}
|
|
8319
|
+
const bytes = await readBinaryFile(runtime.filesystem, imagePath);
|
|
8320
|
+
if (bytes.byteLength > MAX_VIEW_IMAGE_BYTES) {
|
|
8321
|
+
return [
|
|
8322
|
+
{
|
|
8323
|
+
type: "input_text",
|
|
8324
|
+
text: `Codex cannot attach image at \`${toDisplayPath2(imagePath, runtime.cwd)}\`: image exceeds ${MAX_VIEW_IMAGE_BYTES} bytes.`
|
|
8325
|
+
}
|
|
8326
|
+
];
|
|
8327
|
+
}
|
|
8328
|
+
const mimeType = detectImageMimeType(bytes, imagePath);
|
|
8329
|
+
if (!mimeType) {
|
|
8330
|
+
return [
|
|
8331
|
+
{
|
|
8332
|
+
type: "input_text",
|
|
8333
|
+
text: `Codex cannot attach image at \`${toDisplayPath2(imagePath, runtime.cwd)}\`: unsupported image format.`
|
|
8334
|
+
}
|
|
8335
|
+
];
|
|
8336
|
+
}
|
|
8337
|
+
return [
|
|
8338
|
+
{
|
|
8339
|
+
type: "input_image",
|
|
8340
|
+
image_url: `data:${mimeType};base64,${bytes.toString("base64")}`
|
|
8341
|
+
}
|
|
8342
|
+
];
|
|
8343
|
+
}
|
|
8344
|
+
async function readBinaryFile(filesystem, filePath) {
|
|
8345
|
+
if (typeof filesystem.readBinaryFile === "function") {
|
|
8346
|
+
return await filesystem.readBinaryFile(filePath);
|
|
8347
|
+
}
|
|
8348
|
+
const text = await filesystem.readTextFile(filePath);
|
|
8349
|
+
return Buffer4.from(text, "utf8");
|
|
8350
|
+
}
|
|
8351
|
+
function detectImageMimeType(buffer, filePath) {
|
|
8352
|
+
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) {
|
|
8353
|
+
return "image/png";
|
|
8354
|
+
}
|
|
8355
|
+
if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
|
|
8356
|
+
return "image/jpeg";
|
|
8357
|
+
}
|
|
8358
|
+
if (buffer.length >= 6) {
|
|
8359
|
+
const signature = buffer.subarray(0, 6).toString("ascii");
|
|
8360
|
+
if (signature === "GIF87a" || signature === "GIF89a") {
|
|
8361
|
+
return "image/gif";
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8364
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
8365
|
+
return "image/webp";
|
|
8366
|
+
}
|
|
8367
|
+
const fromExtension = IMAGE_MIME_BY_EXTENSION[path5.extname(filePath).toLowerCase()];
|
|
8368
|
+
if (fromExtension && SUPPORTED_IMAGE_MIME_TYPES.has(fromExtension)) {
|
|
8369
|
+
return fromExtension;
|
|
8370
|
+
}
|
|
8371
|
+
return void 0;
|
|
8372
|
+
}
|
|
8373
|
+
function isPdfFile(buffer, filePath) {
|
|
8374
|
+
if (buffer.length >= 5 && buffer.subarray(0, 5).toString("ascii") === "%PDF-") {
|
|
8375
|
+
return true;
|
|
8376
|
+
}
|
|
8377
|
+
return path5.extname(filePath).toLowerCase() === ".pdf";
|
|
8378
|
+
}
|
|
8379
|
+
function isValidUtf8(buffer) {
|
|
8380
|
+
if (buffer.length === 0) {
|
|
8381
|
+
return true;
|
|
8382
|
+
}
|
|
8383
|
+
return Buffer4.from(buffer.toString("utf8"), "utf8").equals(buffer);
|
|
8384
|
+
}
|
|
8238
8385
|
async function readFileGemini(input, options) {
|
|
8239
8386
|
const runtime = resolveRuntime(options);
|
|
8240
8387
|
const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
@@ -8256,56 +8403,6 @@ async function readFileGemini(input, options) {
|
|
|
8256
8403
|
(line, index) => `L${offset + index + 1}: ${truncateAtCodePointBoundary(line ?? "", runtime.maxLineLength)}`
|
|
8257
8404
|
).join("\n");
|
|
8258
8405
|
}
|
|
8259
|
-
async function readFilesGemini(input, options) {
|
|
8260
|
-
const runtime = resolveRuntime(options);
|
|
8261
|
-
const useCharWindow = input.char_offset !== void 0 || input.char_limit !== void 0;
|
|
8262
|
-
const lineOffset = Math.max(0, input.line_offset ?? 0);
|
|
8263
|
-
const lineLimit = input.line_limit ?? DEFAULT_READ_FILES_LINE_LIMIT;
|
|
8264
|
-
const charOffset = Math.max(0, input.char_offset ?? 0);
|
|
8265
|
-
const charLimit = input.char_limit ?? DEFAULT_READ_FILES_CHAR_LIMIT;
|
|
8266
|
-
const includeLineNumbers = input.include_line_numbers !== false;
|
|
8267
|
-
const sections = [];
|
|
8268
|
-
for (const rawPath of input.paths) {
|
|
8269
|
-
const filePath = resolvePathWithPolicy(rawPath, runtime.cwd, runtime.allowOutsideCwd);
|
|
8270
|
-
await runAccessHook2(runtime, {
|
|
8271
|
-
cwd: runtime.cwd,
|
|
8272
|
-
tool: "read_files",
|
|
8273
|
-
action: "read",
|
|
8274
|
-
path: filePath
|
|
8275
|
-
});
|
|
8276
|
-
const content = await runtime.filesystem.readTextFile(filePath);
|
|
8277
|
-
const displayPath = normalizeSlashes(toDisplayPath2(filePath, runtime.cwd));
|
|
8278
|
-
sections.push(`==> ${displayPath} <==`);
|
|
8279
|
-
if (useCharWindow) {
|
|
8280
|
-
if (charOffset >= content.length) {
|
|
8281
|
-
sections.push("");
|
|
8282
|
-
continue;
|
|
8283
|
-
}
|
|
8284
|
-
const end2 = Math.min(content.length, charOffset + charLimit);
|
|
8285
|
-
sections.push(content.slice(charOffset, end2));
|
|
8286
|
-
continue;
|
|
8287
|
-
}
|
|
8288
|
-
const lines = splitLines(content);
|
|
8289
|
-
if (lineOffset >= lines.length) {
|
|
8290
|
-
sections.push("");
|
|
8291
|
-
continue;
|
|
8292
|
-
}
|
|
8293
|
-
const end = Math.min(lines.length, lineOffset + lineLimit);
|
|
8294
|
-
const selected = lines.slice(lineOffset, end);
|
|
8295
|
-
if (includeLineNumbers) {
|
|
8296
|
-
for (let index = 0; index < selected.length; index += 1) {
|
|
8297
|
-
const lineNumber = lineOffset + index + 1;
|
|
8298
|
-
const line = selected[index] ?? "";
|
|
8299
|
-
sections.push(
|
|
8300
|
-
`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`
|
|
8301
|
-
);
|
|
8302
|
-
}
|
|
8303
|
-
continue;
|
|
8304
|
-
}
|
|
8305
|
-
sections.push(selected.join("\n"));
|
|
8306
|
-
}
|
|
8307
|
-
return sections.join("\n");
|
|
8308
|
-
}
|
|
8309
8406
|
async function writeFileGemini(input, options) {
|
|
8310
8407
|
const runtime = resolveRuntime(options);
|
|
8311
8408
|
const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
@@ -8614,117 +8711,6 @@ function truncateAtCodePointBoundary(value, maxLength) {
|
|
|
8614
8711
|
}
|
|
8615
8712
|
return Array.from(value).slice(0, maxLength).join("");
|
|
8616
8713
|
}
|
|
8617
|
-
function measureIndent(line, tabWidth) {
|
|
8618
|
-
let count = 0;
|
|
8619
|
-
for (const char of line) {
|
|
8620
|
-
if (char === " ") {
|
|
8621
|
-
count += 1;
|
|
8622
|
-
continue;
|
|
8623
|
-
}
|
|
8624
|
-
if (char === " ") {
|
|
8625
|
-
count += tabWidth;
|
|
8626
|
-
continue;
|
|
8627
|
-
}
|
|
8628
|
-
break;
|
|
8629
|
-
}
|
|
8630
|
-
return count;
|
|
8631
|
-
}
|
|
8632
|
-
function computeEffectiveIndents(records) {
|
|
8633
|
-
const effective = [];
|
|
8634
|
-
let previous = 0;
|
|
8635
|
-
for (const record of records) {
|
|
8636
|
-
if (record.raw.trim().length === 0) {
|
|
8637
|
-
effective.push(previous);
|
|
8638
|
-
} else {
|
|
8639
|
-
previous = record.indent;
|
|
8640
|
-
effective.push(previous);
|
|
8641
|
-
}
|
|
8642
|
-
}
|
|
8643
|
-
return effective;
|
|
8644
|
-
}
|
|
8645
|
-
function trimBoundaryBlankLines(records) {
|
|
8646
|
-
while (records.length > 0 && records[0]?.raw.trim().length === 0) {
|
|
8647
|
-
records.shift();
|
|
8648
|
-
}
|
|
8649
|
-
while (records.length > 0 && records[records.length - 1]?.raw.trim().length === 0) {
|
|
8650
|
-
records.pop();
|
|
8651
|
-
}
|
|
8652
|
-
}
|
|
8653
|
-
function isCommentLine(line) {
|
|
8654
|
-
const trimmed = line.trim();
|
|
8655
|
-
return trimmed.startsWith("#") || trimmed.startsWith("//") || trimmed.startsWith("--");
|
|
8656
|
-
}
|
|
8657
|
-
function readWithIndentationMode(params) {
|
|
8658
|
-
const { records, anchorLine, limit, maxLevels, includeSiblings, includeHeader, maxLines } = params;
|
|
8659
|
-
const anchorIndex = anchorLine - 1;
|
|
8660
|
-
const effectiveIndents = computeEffectiveIndents(records);
|
|
8661
|
-
const anchorIndent = effectiveIndents[anchorIndex] ?? 0;
|
|
8662
|
-
const minIndent = maxLevels === 0 ? 0 : Math.max(anchorIndent - maxLevels * DEFAULT_TAB_WIDTH, 0);
|
|
8663
|
-
const guardLimit = maxLines ?? limit;
|
|
8664
|
-
const finalLimit = Math.min(limit, guardLimit, records.length);
|
|
8665
|
-
if (finalLimit <= 1) {
|
|
8666
|
-
return [records[anchorIndex]].filter((entry) => Boolean(entry));
|
|
8667
|
-
}
|
|
8668
|
-
let upper = anchorIndex - 1;
|
|
8669
|
-
let lower = anchorIndex + 1;
|
|
8670
|
-
let upperMinIndentHits = 0;
|
|
8671
|
-
let lowerMinIndentHits = 0;
|
|
8672
|
-
const output = [records[anchorIndex]].filter(
|
|
8673
|
-
(entry) => Boolean(entry)
|
|
8674
|
-
);
|
|
8675
|
-
while (output.length < finalLimit) {
|
|
8676
|
-
let progressed = 0;
|
|
8677
|
-
if (upper >= 0) {
|
|
8678
|
-
const candidate = records[upper];
|
|
8679
|
-
const candidateIndent = effectiveIndents[upper] ?? 0;
|
|
8680
|
-
if (candidate && candidateIndent >= minIndent) {
|
|
8681
|
-
output.unshift(candidate);
|
|
8682
|
-
progressed += 1;
|
|
8683
|
-
upper -= 1;
|
|
8684
|
-
if (candidateIndent === minIndent && !includeSiblings) {
|
|
8685
|
-
const allowHeaderComment = includeHeader && isCommentLine(candidate.raw);
|
|
8686
|
-
const canTakeLine = allowHeaderComment || upperMinIndentHits === 0;
|
|
8687
|
-
if (canTakeLine) {
|
|
8688
|
-
upperMinIndentHits += 1;
|
|
8689
|
-
} else {
|
|
8690
|
-
output.shift();
|
|
8691
|
-
progressed -= 1;
|
|
8692
|
-
upper = -1;
|
|
8693
|
-
}
|
|
8694
|
-
}
|
|
8695
|
-
if (output.length >= finalLimit) {
|
|
8696
|
-
break;
|
|
8697
|
-
}
|
|
8698
|
-
} else {
|
|
8699
|
-
upper = -1;
|
|
8700
|
-
}
|
|
8701
|
-
}
|
|
8702
|
-
if (lower < records.length) {
|
|
8703
|
-
const candidate = records[lower];
|
|
8704
|
-
const candidateIndent = effectiveIndents[lower] ?? 0;
|
|
8705
|
-
if (candidate && candidateIndent >= minIndent) {
|
|
8706
|
-
output.push(candidate);
|
|
8707
|
-
progressed += 1;
|
|
8708
|
-
lower += 1;
|
|
8709
|
-
if (candidateIndent === minIndent && !includeSiblings) {
|
|
8710
|
-
if (lowerMinIndentHits > 0) {
|
|
8711
|
-
output.pop();
|
|
8712
|
-
progressed -= 1;
|
|
8713
|
-
lower = records.length;
|
|
8714
|
-
}
|
|
8715
|
-
lowerMinIndentHits += 1;
|
|
8716
|
-
}
|
|
8717
|
-
} else {
|
|
8718
|
-
lower = records.length;
|
|
8719
|
-
}
|
|
8720
|
-
}
|
|
8721
|
-
if (progressed === 0) {
|
|
8722
|
-
break;
|
|
8723
|
-
}
|
|
8724
|
-
}
|
|
8725
|
-
trimBoundaryBlankLines(output);
|
|
8726
|
-
return output;
|
|
8727
|
-
}
|
|
8728
8714
|
async function collectDirectoryEntries(filesystem, rootPath, depth, maxLineLength) {
|
|
8729
8715
|
const queue = [
|
|
8730
8716
|
{ path: rootPath, relativePrefix: "", remainingDepth: depth }
|
|
@@ -10042,10 +10028,10 @@ export {
|
|
|
10042
10028
|
createListDirectoryTool,
|
|
10043
10029
|
createModelAgnosticFilesystemToolSet,
|
|
10044
10030
|
createNodeAgentFilesystem,
|
|
10045
|
-
createReadFilesTool,
|
|
10046
10031
|
createReplaceTool,
|
|
10047
10032
|
createRgSearchTool,
|
|
10048
10033
|
createToolLoopSteeringChannel,
|
|
10034
|
+
createViewImageTool,
|
|
10049
10035
|
createWriteFileTool,
|
|
10050
10036
|
customTool,
|
|
10051
10037
|
encodeChatGptAuthJson,
|