deepagents 1.13.0 → 1.13.2
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/{agent-LUvc15zz.d.cts → agent-BwYXPTyT.d.cts} +50 -15
- package/dist/{agent-Bx0owr-O.d.ts → agent-CH8GB5HD.d.ts} +50 -15
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/index.cjs +3 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/{langsmith-zm0ILQsV.js → langsmith-BBV5JlNW.js} +227 -45
- package/dist/langsmith-BBV5JlNW.js.map +1 -0
- package/dist/{langsmith-Ck9t7AGW.cjs → langsmith-DL32swQ3.cjs} +231 -43
- package/dist/langsmith-DL32swQ3.cjs.map +1 -0
- package/dist/node.cjs +3 -2
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/{src-C9o7b5C6.js → src-CKk0apeE.js} +18 -7
- package/dist/src-CKk0apeE.js.map +1 -0
- package/dist/{src-C97zNx7s.cjs → src-DdGDIhZk.cjs} +18 -7
- package/dist/src-DdGDIhZk.cjs.map +1 -0
- package/package.json +2 -2
- package/dist/langsmith-Ck9t7AGW.cjs.map +0 -1
- package/dist/langsmith-zm0ILQsV.js.map +0 -1
- package/dist/src-C97zNx7s.cjs.map +0 -1
- package/dist/src-C9o7b5C6.js.map +0 -1
|
@@ -47,6 +47,22 @@ const EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty conten
|
|
|
47
47
|
const MAX_LINE_LENGTH = 5e3;
|
|
48
48
|
const TOOL_RESULT_TOKEN_LIMIT = 2e4;
|
|
49
49
|
const TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
|
|
50
|
+
/**
|
|
51
|
+
* Normalize model- or caller-supplied text pagination bounds.
|
|
52
|
+
*
|
|
53
|
+
* Every backend must slice content and calculate pagination metadata from the
|
|
54
|
+
* same normalized values. Otherwise a fractional or negative argument could
|
|
55
|
+
* return one window while advertising a different `nextOffset`.
|
|
56
|
+
*
|
|
57
|
+
* Binary reads do not use this helper because their backend contract ignores
|
|
58
|
+
* line-based offset and limit values.
|
|
59
|
+
*/
|
|
60
|
+
function normalizeReadPagination(offset, limit) {
|
|
61
|
+
return {
|
|
62
|
+
offset: Number.isFinite(offset) ? Math.max(0, Math.floor(offset)) : 0,
|
|
63
|
+
limit: Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 0
|
|
64
|
+
};
|
|
65
|
+
}
|
|
50
66
|
const MIME_TYPES = {
|
|
51
67
|
".png": "image/png",
|
|
52
68
|
".jpg": "image/jpeg",
|
|
@@ -158,40 +174,60 @@ function sanitizeToolCallId(toolCallId) {
|
|
|
158
174
|
return toolCallId.replace(/\./g, "_").replace(/\//g, "_").replace(/\\/g, "_");
|
|
159
175
|
}
|
|
160
176
|
/**
|
|
161
|
-
* Format file content with line numbers
|
|
162
|
-
*
|
|
163
|
-
* Chunks lines longer than MAX_LINE_LENGTH with continuation markers (e.g., 5.1, 5.2).
|
|
177
|
+
* Format file content with line numbers and structured source-line boundaries.
|
|
164
178
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
179
|
+
* The boundaries let downstream size limiting truncate only after complete
|
|
180
|
+
* source lines without reparsing the rendered gutter. This keeps presentation
|
|
181
|
+
* details (padding, tab separators, and continuation labels) encapsulated in
|
|
182
|
+
* the formatter that creates them.
|
|
168
183
|
*/
|
|
169
|
-
function
|
|
184
|
+
function formatContentWithLineNumbersAndBoundaries(content, startLine = 1) {
|
|
170
185
|
let lines;
|
|
171
186
|
if (typeof content === "string") {
|
|
172
187
|
lines = content.split("\n");
|
|
173
188
|
if (lines.length > 0 && lines[lines.length - 1] === "") lines = lines.slice(0, -1);
|
|
174
189
|
} else lines = content;
|
|
175
190
|
const resultLines = [];
|
|
191
|
+
const sourceLineBoundaries = [];
|
|
192
|
+
let renderedLength = 0;
|
|
193
|
+
const appendRow = (row, sourceLine, completesSourceLine) => {
|
|
194
|
+
if (resultLines.length > 0) renderedLength += 1;
|
|
195
|
+
resultLines.push(row);
|
|
196
|
+
renderedLength += row.length;
|
|
197
|
+
if (completesSourceLine) sourceLineBoundaries.push({
|
|
198
|
+
sourceLine,
|
|
199
|
+
endOffset: renderedLength
|
|
200
|
+
});
|
|
201
|
+
};
|
|
176
202
|
for (let i = 0; i < lines.length; i++) {
|
|
177
203
|
const line = lines[i];
|
|
178
204
|
const lineNum = i + startLine;
|
|
179
|
-
if (line.length <= 5e3)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
resultLines.push(`${continuationMarker.padStart(6)}\t${chunk}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
205
|
+
if (line.length <= 5e3) {
|
|
206
|
+
appendRow(`${lineNum.toString().padStart(6)}\t${line}`, lineNum, true);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const numChunks = Math.ceil(line.length / MAX_LINE_LENGTH);
|
|
210
|
+
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
211
|
+
const start = chunkIdx * MAX_LINE_LENGTH;
|
|
212
|
+
const end = Math.min(start + MAX_LINE_LENGTH, line.length);
|
|
213
|
+
const chunk = line.substring(start, end);
|
|
214
|
+
appendRow(`${(chunkIdx === 0 ? `${lineNum}` : `${lineNum}.${chunkIdx}`).padStart(6)}\t${chunk}`, lineNum, chunkIdx === numChunks - 1);
|
|
192
215
|
}
|
|
193
216
|
}
|
|
194
|
-
return
|
|
217
|
+
return {
|
|
218
|
+
text: resultLines.join("\n"),
|
|
219
|
+
sourceLineBoundaries
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Format file content with line numbers (cat -n style).
|
|
224
|
+
*
|
|
225
|
+
* Lines longer than `MAX_LINE_LENGTH` are split into continuation rows such as
|
|
226
|
+
* `5.1` and `5.2`. Use `formatContentWithLineNumbersAndBoundaries` when a
|
|
227
|
+
* caller also needs safe source-line truncation points.
|
|
228
|
+
*/
|
|
229
|
+
function formatContentWithLineNumbers(content, startLine = 1) {
|
|
230
|
+
return formatContentWithLineNumbersAndBoundaries(content, startLine).text;
|
|
195
231
|
}
|
|
196
232
|
/**
|
|
197
233
|
* Check if content is empty and return warning message.
|
|
@@ -865,10 +901,23 @@ var StateBackend = class {
|
|
|
865
901
|
mimeType: fileDataV2.mimeType
|
|
866
902
|
};
|
|
867
903
|
if (typeof fileDataV2.content !== "string") return { error: `File '${filePath}' has binary content but text MIME type` };
|
|
868
|
-
|
|
869
|
-
|
|
904
|
+
const { offset: normalizedOffset, limit: normalizedLimit } = normalizeReadPagination(offset, limit);
|
|
905
|
+
const lines = fileDataV2.content.split("\n");
|
|
906
|
+
const totalLines = lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
907
|
+
const selected = lines.slice(normalizedOffset, normalizedOffset + normalizedLimit);
|
|
908
|
+
if (selected.length === 0 || normalizedOffset >= totalLines || normalizedLimit === 0) return {
|
|
909
|
+
content: selected.join("\n"),
|
|
870
910
|
mimeType: fileDataV2.mimeType
|
|
871
911
|
};
|
|
912
|
+
const endOffset = Math.min(normalizedOffset + selected.length, totalLines);
|
|
913
|
+
return {
|
|
914
|
+
content: selected.join("\n"),
|
|
915
|
+
mimeType: fileDataV2.mimeType,
|
|
916
|
+
totalLines,
|
|
917
|
+
startLine: normalizedOffset + 1,
|
|
918
|
+
endLine: endOffset,
|
|
919
|
+
nextOffset: endOffset < totalLines ? endOffset : void 0
|
|
920
|
+
};
|
|
872
921
|
}
|
|
873
922
|
/**
|
|
874
923
|
* Read file content as raw FileData.
|
|
@@ -1584,6 +1633,73 @@ const READ_FILE_TRUNCATION_MSG = `
|
|
|
1584
1633
|
|
|
1585
1634
|
[Output was truncated due to size limits. The file content is very large. Consider reformatting the file to make it easier to navigate. For example, if this is JSON, use execute(command='jq . {file_path}') to pretty-print it with line breaks. For other formats, you can use appropriate formatting tools to split long lines.]`;
|
|
1586
1635
|
/**
|
|
1636
|
+
* Render backend pagination metadata as guidance for the model.
|
|
1637
|
+
*
|
|
1638
|
+
* Backends own source-level pagination because only they know how much of the
|
|
1639
|
+
* file was read. The middleware owns presentation: it line-numbers the text
|
|
1640
|
+
* and turns optional metadata into a human-readable footer. Keeping the fields
|
|
1641
|
+
* optional preserves compatibility with custom backends that predate this
|
|
1642
|
+
* contract; those reads simply receive no pagination footer.
|
|
1643
|
+
*
|
|
1644
|
+
* `nextOffset` is the signal that the read is partial. A result at EOF omits it,
|
|
1645
|
+
* so complete reads retain their previous output shape.
|
|
1646
|
+
*/
|
|
1647
|
+
function remainingLinesNotice(readResult) {
|
|
1648
|
+
const { startLine, endLine, nextOffset, totalLines } = readResult;
|
|
1649
|
+
if (startLine === void 0 || endLine === void 0 || nextOffset === void 0 || !Number.isSafeInteger(startLine) || !Number.isSafeInteger(endLine) || !Number.isSafeInteger(nextOffset) || startLine < 1 || endLine < startLine || nextOffset !== endLine || totalLines !== void 0 && (!Number.isSafeInteger(totalLines) || totalLines < endLine)) return "";
|
|
1650
|
+
const readCount = endLine - startLine + 1;
|
|
1651
|
+
const readUnit = readCount === 1 ? "line" : "lines";
|
|
1652
|
+
if (totalLines === void 0) return `\n\n[Read ${readCount} ${readUnit} (lines ${startLine}-${endLine}). More lines remain from offset ${nextOffset}.]`;
|
|
1653
|
+
if (endLine >= totalLines) return "";
|
|
1654
|
+
const remaining = totalLines - endLine;
|
|
1655
|
+
return `\n\n[Read ${readCount} ${readUnit} (lines ${startLine}-${endLine} of ${totalLines} total). ${remaining} ${remaining === 1 ? "line" : "lines"} remaining from offset ${nextOffset}.]`;
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Fit a line-numbered read into the middleware's output budget without
|
|
1659
|
+
* publishing a resume offset that skips content the model did not see.
|
|
1660
|
+
*
|
|
1661
|
+
* There are two independent forms of limiting:
|
|
1662
|
+
*
|
|
1663
|
+
* 1. The backend paginates the source file with `offset` and `limit`.
|
|
1664
|
+
* 2. The middleware may further shorten that page to fit its token budget.
|
|
1665
|
+
*
|
|
1666
|
+
* If the backend returned lines 1-100 but the middleware only displayed lines
|
|
1667
|
+
* 1-30, forwarding the backend's original `nextOffset: 100` would silently skip
|
|
1668
|
+
* lines 31-100 on the next read. This function therefore truncates only after a
|
|
1669
|
+
* complete source line and rebuilds the remaining-lines notice using the last
|
|
1670
|
+
* line actually displayed.
|
|
1671
|
+
*
|
|
1672
|
+
* Long source lines may occupy several formatted rows (`12`, `12.1`, ...). The
|
|
1673
|
+
* formatter records a structured boundary only after the final chunk, so this
|
|
1674
|
+
* function does not need to inspect or understand the gutter representation.
|
|
1675
|
+
* If no complete source line can fit beside the truncation message, the function
|
|
1676
|
+
* falls back to character truncation and omits pagination guidance rather than
|
|
1677
|
+
* advertising an unsafe offset.
|
|
1678
|
+
*/
|
|
1679
|
+
function truncatePaginatedRead(formatted, filePath, readResult, tokenLimit) {
|
|
1680
|
+
const content = formatted.text;
|
|
1681
|
+
const notice = remainingLinesNotice(readResult);
|
|
1682
|
+
if (!tokenLimit || content.length + notice.length < 4 * tokenLimit) return content + notice;
|
|
1683
|
+
const truncationMsg = READ_FILE_TRUNCATION_MSG.replace("{file_path}", filePath);
|
|
1684
|
+
const threshold = 4 * tokenLimit;
|
|
1685
|
+
if (readResult.startLine !== void 0 && readResult.endLine !== void 0) {
|
|
1686
|
+
const finalSourceLine = readResult.endLine;
|
|
1687
|
+
const boundaries = formatted.sourceLineBoundaries.filter((boundary) => boundary.sourceLine <= finalSourceLine);
|
|
1688
|
+
for (let index = boundaries.length - 1; index >= 0; index -= 1) {
|
|
1689
|
+
const boundary = boundaries[index];
|
|
1690
|
+
const adjustedNotice = remainingLinesNotice({
|
|
1691
|
+
totalLines: readResult.totalLines,
|
|
1692
|
+
startLine: readResult.startLine,
|
|
1693
|
+
endLine: boundary.sourceLine,
|
|
1694
|
+
nextOffset: boundary.sourceLine
|
|
1695
|
+
});
|
|
1696
|
+
if (boundary.endOffset + truncationMsg.length + adjustedNotice.length <= threshold) return content.slice(0, boundary.endOffset) + truncationMsg + adjustedNotice;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
const maxContentLength = Math.max(0, threshold - truncationMsg.length);
|
|
1700
|
+
return content.substring(0, maxContentLength) + truncationMsg;
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1587
1703
|
* Note appended to grep results that were cut short by the match-count cap.
|
|
1588
1704
|
*/
|
|
1589
1705
|
const GREP_TRUNCATION_NOTE = "Note: the search stopped early because it hit the maximum match count. The matches above are valid but incomplete. Narrow the search (a more specific pattern or a narrower path), or raise max_count, to see the rest.";
|
|
@@ -2085,7 +2201,8 @@ function createReadFileTool(backend, options) {
|
|
|
2085
2201
|
const permissionError = checkPermission(permissions, "read", input.file_path);
|
|
2086
2202
|
if (permissionError !== void 0) return toolError(runtime, "read_file", permissionError);
|
|
2087
2203
|
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
2088
|
-
const { file_path, offset = 0, limit = 100 } = input;
|
|
2204
|
+
const { file_path, offset: requestedOffset = 0, limit: requestedLimit = 100 } = input;
|
|
2205
|
+
const { offset, limit } = normalizeReadPagination(requestedOffset, requestedLimit);
|
|
2089
2206
|
const readResult = await resolvedBackend.read(file_path, offset, limit);
|
|
2090
2207
|
if (readResult.error) return [{
|
|
2091
2208
|
type: "text",
|
|
@@ -2133,16 +2250,21 @@ function createReadFileTool(backend, options) {
|
|
|
2133
2250
|
}
|
|
2134
2251
|
let content = typeof readResult.content === "string" ? readResult.content : "";
|
|
2135
2252
|
const lines = content.split("\n");
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2253
|
+
let paginationResult = readResult;
|
|
2254
|
+
if (lines.length > limit) {
|
|
2255
|
+
content = lines.slice(0, limit).join("\n");
|
|
2256
|
+
if (limit > 0 && readResult.startLine !== void 0 && readResult.endLine !== void 0) {
|
|
2257
|
+
const endLine = Math.min(readResult.startLine + limit - 1, readResult.endLine, readResult.totalLines ?? Number.POSITIVE_INFINITY);
|
|
2258
|
+
paginationResult = {
|
|
2259
|
+
...readResult,
|
|
2260
|
+
endLine,
|
|
2261
|
+
nextOffset: endLine
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2142
2264
|
}
|
|
2143
2265
|
return [{
|
|
2144
2266
|
type: "text",
|
|
2145
|
-
text:
|
|
2267
|
+
text: truncatePaginatedRead(formatContentWithLineNumbersAndBoundaries(content, paginationResult.startLine ?? offset + 1), file_path, paginationResult, toolTokenLimitBeforeEvict)
|
|
2146
2268
|
}];
|
|
2147
2269
|
}, {
|
|
2148
2270
|
name: "read_file",
|
|
@@ -5476,8 +5598,9 @@ function hasToolName(tool) {
|
|
|
5476
5598
|
return tool !== null && typeof tool === "object" && "name" in tool && typeof tool.name === "string";
|
|
5477
5599
|
}
|
|
5478
5600
|
/**
|
|
5479
|
-
* Create middleware that
|
|
5480
|
-
*
|
|
5601
|
+
* Create middleware that hides excluded tools from the model and rejects calls
|
|
5602
|
+
* to them. Exclusions calibrate the agent per model; they are not a security
|
|
5603
|
+
* boundary.
|
|
5481
5604
|
*
|
|
5482
5605
|
* @internal
|
|
5483
5606
|
*/
|
|
@@ -5489,6 +5612,16 @@ function createToolExclusionMiddleware(excludedTools) {
|
|
|
5489
5612
|
...request,
|
|
5490
5613
|
tools: request.tools?.filter((tool) => !hasToolName(tool) || !excludedTools.has(tool.name))
|
|
5491
5614
|
});
|
|
5615
|
+
},
|
|
5616
|
+
wrapToolCall(request, handler) {
|
|
5617
|
+
const { name, id } = request.toolCall;
|
|
5618
|
+
if (!excludedTools.has(name)) return handler(request);
|
|
5619
|
+
return new _langchain_core_messages.ToolMessage({
|
|
5620
|
+
content: `Error: ${name} is not available.`,
|
|
5621
|
+
tool_call_id: id ?? "",
|
|
5622
|
+
name,
|
|
5623
|
+
status: "error"
|
|
5624
|
+
});
|
|
5492
5625
|
}
|
|
5493
5626
|
});
|
|
5494
5627
|
}
|
|
@@ -6800,10 +6933,23 @@ var StoreBackend = class {
|
|
|
6800
6933
|
mimeType: fileDataV2.mimeType
|
|
6801
6934
|
};
|
|
6802
6935
|
if (typeof fileDataV2.content !== "string") return { error: `File '${filePath}' has binary content but text MIME type` };
|
|
6803
|
-
|
|
6804
|
-
|
|
6936
|
+
const { offset: normalizedOffset, limit: normalizedLimit } = normalizeReadPagination(offset, limit);
|
|
6937
|
+
const lines = fileDataV2.content.split("\n");
|
|
6938
|
+
const totalLines = lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
6939
|
+
const selected = lines.slice(normalizedOffset, normalizedOffset + normalizedLimit);
|
|
6940
|
+
if (selected.length === 0 || normalizedOffset >= totalLines || normalizedLimit === 0) return {
|
|
6941
|
+
content: selected.join("\n"),
|
|
6805
6942
|
mimeType: fileDataV2.mimeType
|
|
6806
6943
|
};
|
|
6944
|
+
const endOffset = Math.min(normalizedOffset + selected.length, totalLines);
|
|
6945
|
+
return {
|
|
6946
|
+
content: selected.join("\n"),
|
|
6947
|
+
mimeType: fileDataV2.mimeType,
|
|
6948
|
+
totalLines,
|
|
6949
|
+
startLine: normalizedOffset + 1,
|
|
6950
|
+
endLine: endOffset,
|
|
6951
|
+
nextOffset: endOffset < totalLines ? endOffset : void 0
|
|
6952
|
+
};
|
|
6807
6953
|
} catch (e) {
|
|
6808
6954
|
return { error: e.message };
|
|
6809
6955
|
}
|
|
@@ -7069,12 +7215,20 @@ function splitLinesKeepEnds(content) {
|
|
|
7069
7215
|
return lines;
|
|
7070
7216
|
}
|
|
7071
7217
|
function sliceReadContent(content, offset, limit) {
|
|
7072
|
-
if (!content
|
|
7218
|
+
if (!content) return { content };
|
|
7073
7219
|
const lines = splitLinesKeepEnds(content.replace(/\r\n/g, "\n").replace(/\r/g, "\n"));
|
|
7074
7220
|
const startIndex = offset;
|
|
7075
7221
|
const endIndex = Math.min(startIndex + limit, lines.length);
|
|
7076
7222
|
if (startIndex >= lines.length) return { error: `Line offset ${offset} exceeds file length (${lines.length} lines)` };
|
|
7077
|
-
|
|
7223
|
+
const selected = lines.slice(startIndex, endIndex);
|
|
7224
|
+
if (selected.length === 0 || offset < 0 || limit <= 0) return { content: selected.join("") };
|
|
7225
|
+
return {
|
|
7226
|
+
content: selected.join(""),
|
|
7227
|
+
totalLines: lines.length,
|
|
7228
|
+
startLine: startIndex + 1,
|
|
7229
|
+
endLine: endIndex,
|
|
7230
|
+
nextOffset: endIndex < lines.length ? endIndex : void 0
|
|
7231
|
+
};
|
|
7078
7232
|
}
|
|
7079
7233
|
function isLangSmithNotFoundError(error) {
|
|
7080
7234
|
if (typeof error !== "object" || error === null) return false;
|
|
@@ -7553,9 +7707,11 @@ var ContextHubBackend = class ContextHubBackend {
|
|
|
7553
7707
|
}
|
|
7554
7708
|
const content = cache[hubPath];
|
|
7555
7709
|
if (content === void 0) return { error: `File '${filePath}' not found` };
|
|
7556
|
-
const
|
|
7710
|
+
const { offset: normalizedOffset, limit: normalizedLimit } = normalizeReadPagination(offset, limit);
|
|
7711
|
+
const sliced = sliceReadContent(content, normalizedOffset, normalizedLimit);
|
|
7557
7712
|
if (sliced.error) return { error: sliced.error };
|
|
7558
7713
|
return {
|
|
7714
|
+
...sliced,
|
|
7559
7715
|
content: sliced.content ?? "",
|
|
7560
7716
|
mimeType: TEXT_MIME_TYPE
|
|
7561
7717
|
};
|
|
@@ -7894,6 +8050,7 @@ function buildFindCommand(searchPath) {
|
|
|
7894
8050
|
const findBase = `find -L ${quotedPath} ${`\\( -path /proc -o -path /sys -o -path /dev -o -path /run -o -path ${quotedPath}/proc -o -path ${quotedPath}/sys -o -path ${quotedPath}/dev -o -path ${quotedPath}/run \\) -prune`} -o -not -path ${quotedPath}`;
|
|
7895
8051
|
return `{ ${`if find /dev/null -maxdepth 0 -printf '' 2>/dev/null; then ${findBase} -printf '%s\\t%T@\\t%y\\t%p\\n' 2>/dev/null; elif stat -c %s /dev/null >/dev/null 2>&1; then ${findBase} -exec sh -c '${STAT_C_SCRIPT}' _ {} +; else ${findBase} -exec stat -f '%z\t%m\t%Sp\t%N' {} + 2>/dev/null; fi || true`}; } | head -n 50001`;
|
|
7896
8052
|
}
|
|
8053
|
+
const READ_METADATA_PREFIX = "__DEEPAGENTS_READ_METADATA__";
|
|
7897
8054
|
/**
|
|
7898
8055
|
* Pure POSIX shell command for reading files with line numbers.
|
|
7899
8056
|
* Uses awk for line numbering with offset/limit — works on any Linux including Alpine.
|
|
@@ -7907,9 +8064,32 @@ function buildReadCommand(filePath, offset, limit) {
|
|
|
7907
8064
|
return [
|
|
7908
8065
|
`if [ ! -f ${quotedPath} ]; then echo "Error: File not found"; exit 1; fi`,
|
|
7909
8066
|
`if [ ! -s ${quotedPath} ]; then echo "System reminder: File exists but has empty contents"; exit 0; fi`,
|
|
7910
|
-
`awk 'NR >= ${start} && NR <= ${end} { printf "%6d\\t%s\\n", NR, $0 }' ${quotedPath}`
|
|
8067
|
+
`awk 'NR >= ${start} && NR <= ${end} { printf "%6d\\t%s\\n", NR, $0 } END { printf "${READ_METADATA_PREFIX}\\t%d\\n", NR }' ${quotedPath}`
|
|
7911
8068
|
].join("; ");
|
|
7912
8069
|
}
|
|
8070
|
+
function parseReadOutput(output, offset, limit) {
|
|
8071
|
+
const rows = output.split("\n");
|
|
8072
|
+
let metadataIndex = -1;
|
|
8073
|
+
for (let index = rows.length - 1; index >= 0; index -= 1) if (rows[index].startsWith(`${READ_METADATA_PREFIX}\t`)) {
|
|
8074
|
+
metadataIndex = index;
|
|
8075
|
+
break;
|
|
8076
|
+
}
|
|
8077
|
+
if (metadataIndex === -1) return { content: output };
|
|
8078
|
+
const totalLines = Number(rows[metadataIndex].slice(29));
|
|
8079
|
+
if (!Number.isSafeInteger(totalLines) || totalLines < 0) return { content: output };
|
|
8080
|
+
const contentRows = rows.slice(0, metadataIndex);
|
|
8081
|
+
const content = contentRows.length > 0 ? `${contentRows.join("\n")}\n` : "";
|
|
8082
|
+
const startOffset = Math.floor(offset);
|
|
8083
|
+
const endOffset = Math.min(startOffset + Math.floor(limit), totalLines);
|
|
8084
|
+
if (startOffset >= totalLines || endOffset <= startOffset) return { content };
|
|
8085
|
+
return {
|
|
8086
|
+
content,
|
|
8087
|
+
totalLines,
|
|
8088
|
+
startLine: startOffset + 1,
|
|
8089
|
+
endLine: endOffset,
|
|
8090
|
+
nextOffset: endOffset < totalLines ? endOffset : void 0
|
|
8091
|
+
};
|
|
8092
|
+
}
|
|
7913
8093
|
/**
|
|
7914
8094
|
* Build a grep command for literal (fixed-string) search.
|
|
7915
8095
|
* Uses grep -rHnF for recursive, with-filename, with-line-number, fixed-string search.
|
|
@@ -7987,15 +8167,17 @@ var BaseSandbox = class {
|
|
|
7987
8167
|
mimeType
|
|
7988
8168
|
};
|
|
7989
8169
|
}
|
|
7990
|
-
|
|
8170
|
+
const { offset: normalizedOffset, limit: normalizedLimit } = normalizeReadPagination(offset, limit);
|
|
8171
|
+
if (normalizedLimit === 0) return {
|
|
7991
8172
|
content: "",
|
|
7992
8173
|
mimeType
|
|
7993
8174
|
};
|
|
7994
|
-
const command = buildReadCommand(filePath,
|
|
8175
|
+
const command = buildReadCommand(filePath, normalizedOffset, normalizedLimit);
|
|
7995
8176
|
const result = await this.execute(command);
|
|
7996
8177
|
if (result.exitCode !== 0) return { error: `File '${filePath}' not found` };
|
|
8178
|
+
const parsed = parseReadOutput(result.output, normalizedOffset, normalizedLimit);
|
|
7997
8179
|
return {
|
|
7998
|
-
content:
|
|
8180
|
+
...result.truncated ? { content: parsed.content } : parsed,
|
|
7999
8181
|
mimeType
|
|
8000
8182
|
};
|
|
8001
8183
|
}
|
|
@@ -8712,6 +8894,12 @@ Object.defineProperty(exports, "isTextMimeType", {
|
|
|
8712
8894
|
return isTextMimeType;
|
|
8713
8895
|
}
|
|
8714
8896
|
});
|
|
8897
|
+
Object.defineProperty(exports, "normalizeReadPagination", {
|
|
8898
|
+
enumerable: true,
|
|
8899
|
+
get: function() {
|
|
8900
|
+
return normalizeReadPagination;
|
|
8901
|
+
}
|
|
8902
|
+
});
|
|
8715
8903
|
Object.defineProperty(exports, "parseHarnessProfileConfig", {
|
|
8716
8904
|
enumerable: true,
|
|
8717
8905
|
get: function() {
|
|
@@ -8743,4 +8931,4 @@ Object.defineProperty(exports, "serializeProfile", {
|
|
|
8743
8931
|
}
|
|
8744
8932
|
});
|
|
8745
8933
|
|
|
8746
|
-
//# sourceMappingURL=langsmith-
|
|
8934
|
+
//# sourceMappingURL=langsmith-DL32swQ3.cjs.map
|