deepagents 1.12.1 → 1.12.3

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.
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
19
  value: mod,
20
20
  enumerable: true
21
21
  }) : target, mod));
@@ -326,13 +326,13 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
326
326
  function truncateIfTooLong(result) {
327
327
  if (Array.isArray(result)) {
328
328
  const totalChars = result.reduce((sum, item) => sum + item.length, 0);
329
- if (totalChars > 2e4 * 4) {
329
+ if (totalChars > 8e4) {
330
330
  const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
331
331
  return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
332
332
  }
333
333
  return result;
334
334
  }
335
- if (result.length > 2e4 * 4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
335
+ if (result.length > 8e4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
336
336
  return result;
337
337
  }
338
338
  /**
@@ -425,6 +425,30 @@ function globSearchFiles(files, pattern, path = "/") {
425
425
  return matches.map(([fp]) => fp).join("\n");
426
426
  }
427
427
  /**
428
+ * Format grep search results based on output mode.
429
+ *
430
+ * @param results - Dictionary mapping file paths to list of [line_num, line_content] tuples
431
+ * @param outputMode - Output format - "files_with_matches", "content", or "count"
432
+ * @returns Formatted string output
433
+ */
434
+ function formatGrepResults(results, outputMode) {
435
+ if (outputMode === "files_with_matches") return Object.keys(results).sort().join("\n");
436
+ if (outputMode === "count") {
437
+ const lines = [];
438
+ for (const filePath of Object.keys(results).sort()) {
439
+ const count = results[filePath].length;
440
+ lines.push(`${filePath}: ${count}`);
441
+ }
442
+ return lines.join("\n");
443
+ }
444
+ const lines = [];
445
+ for (const filePath of Object.keys(results).sort()) {
446
+ lines.push(`${filePath}:`);
447
+ for (const [lineNum, line] of results[filePath]) lines.push(` ${lineNum}: ${line}`);
448
+ }
449
+ return lines.join("\n");
450
+ }
451
+ /**
428
452
  * Return structured grep matches from an in-memory files mapping.
429
453
  *
430
454
  * Performs literal text search (not regex). Binary files are skipped.
@@ -455,6 +479,24 @@ function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
455
479
  return matches;
456
480
  }
457
481
  /**
482
+ * Group structured matches into the legacy dict form used by formatters.
483
+ */
484
+ function buildGrepResultsDict(matches) {
485
+ const grouped = {};
486
+ for (const m of matches) {
487
+ if (!grouped[m.path]) grouped[m.path] = [];
488
+ grouped[m.path].push([m.line, m.text]);
489
+ }
490
+ return grouped;
491
+ }
492
+ /**
493
+ * Format structured grep matches using existing formatting logic.
494
+ */
495
+ function formatGrepMatches(matches, outputMode) {
496
+ if (matches.length === 0) return "No matches found";
497
+ return formatGrepResults(buildGrepResultsDict(matches), outputMode);
498
+ }
499
+ /**
458
500
  * Determine MIME type from a file path's extension.
459
501
  *
460
502
  * Defaults to "text/plain" for unknown extensions. Only the known non-text
@@ -554,9 +596,12 @@ function adaptBackendProtocol(backend) {
554
596
  if (typeof result === "string") return { content: result };
555
597
  return result;
556
598
  },
557
- async grep(pattern, path, glob) {
558
- const result = await ("grep" in backend ? backend.grep(pattern, path, glob) : backend.grepRaw(pattern, path, glob));
559
- if (Array.isArray(result)) return { matches: result };
599
+ async grep(pattern, path, glob, maxCount) {
600
+ const result = await ("grep" in backend ? backend.grep(pattern, path, glob, maxCount) : backend.grepRaw(pattern, path, glob));
601
+ if (Array.isArray(result)) return applyGrepMaxCount({
602
+ result: { matches: result },
603
+ maxCount
604
+ });
560
605
  if (typeof result === "string") return { error: result };
561
606
  return result;
562
607
  }
@@ -591,6 +636,21 @@ function adaptSandboxProtocol(sandbox) {
591
636
  //#endregion
592
637
  //#region src/backends/protocol.ts
593
638
  /**
639
+ * Enforce a match cap after a backend grep has completed.
640
+ *
641
+ * When `maxCount` is set and the result exceeds it, the matches are sliced
642
+ * to the cap and the result is flagged `truncated: true`.
643
+ */
644
+ function applyGrepMaxCount(params) {
645
+ const { result, maxCount } = params;
646
+ if (maxCount == null || result.matches == null || result.matches.length <= maxCount) return result;
647
+ return {
648
+ error: result.error,
649
+ matches: result.matches.slice(0, maxCount),
650
+ truncated: true
651
+ };
652
+ }
653
+ /**
594
654
  * Type guard to check if a backend supports execution.
595
655
  *
596
656
  * @param backend - Backend instance to check
@@ -871,9 +931,12 @@ var StateBackend = class {
871
931
  * Search file contents for a literal text pattern.
872
932
  * Binary files are skipped.
873
933
  */
874
- grep(pattern, path = "/", glob = null) {
934
+ grep(pattern, path = "/", glob = null, maxCount = null) {
875
935
  const files = this.files;
876
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
936
+ return applyGrepMaxCount({
937
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
938
+ maxCount
939
+ });
877
940
  }
878
941
  /**
879
942
  * Structured glob matching returning FileInfo objects.
@@ -1115,7 +1178,7 @@ var CompositeBackend = class {
1115
1178
  const results = [];
1116
1179
  const defaultResult = await this.default.ls(path);
1117
1180
  if (defaultResult.error) return defaultResult;
1118
- results.push(...defaultResult.files || []);
1181
+ for (const fi of defaultResult.files || []) results.push(fi);
1119
1182
  for (const [routePrefix] of this.sortedRoutes) results.push({
1120
1183
  path: routePrefix,
1121
1184
  is_dir: true,
@@ -1151,33 +1214,56 @@ var CompositeBackend = class {
1151
1214
  }
1152
1215
  /**
1153
1216
  * Structured search results or error string for invalid input.
1217
+ *
1218
+ * @param maxCount - Optional total cap on returned matches across all routed
1219
+ * backends. When the cap is reached, remaining routes are
1220
+ * short-circuited and the result is flagged `truncated: true`.
1154
1221
  */
1155
- async grep(pattern, path = "/", glob = null) {
1222
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
1156
1223
  const searchPath = path || "/";
1157
1224
  for (const [routePrefix, backend] of this.sortedRoutes) if (this.isPathWithinRoute(searchPath, routePrefix)) {
1158
1225
  const routeSearchPath = searchPath.substring(routePrefix.length - 1);
1159
- const raw = await backend.grep(pattern, routeSearchPath || "/", glob);
1226
+ const raw = await backend.grep(pattern, routeSearchPath || "/", glob, maxCount);
1160
1227
  if (raw.error) return raw;
1161
- return { matches: (raw.matches || []).map((m) => ({
1162
- ...m,
1163
- path: routePrefix.slice(0, -1) + m.path
1164
- })) };
1228
+ return applyGrepMaxCount({
1229
+ result: {
1230
+ matches: (raw.matches || []).map((m) => ({
1231
+ ...m,
1232
+ path: routePrefix.slice(0, -1) + m.path
1233
+ })),
1234
+ truncated: raw.truncated
1235
+ },
1236
+ maxCount
1237
+ });
1165
1238
  }
1166
1239
  const allMatches = [];
1167
- const rawDefault = await this.default.grep(pattern, searchPath, glob);
1240
+ let truncated = false;
1241
+ const rawDefault = await this.default.grep(pattern, searchPath, glob, maxCount);
1168
1242
  if (rawDefault.error) return rawDefault;
1169
- allMatches.push(...rawDefault.matches || []);
1243
+ for (const m of rawDefault.matches || []) allMatches.push(m);
1244
+ truncated = truncated || rawDefault.truncated === true;
1170
1245
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1171
1246
  if (!this.isRouteUnderPath(routePrefix, searchPath)) continue;
1172
- const raw = await backend.grep(pattern, "/", glob);
1247
+ const remaining = maxCount == null ? null : Math.max(maxCount - allMatches.length, 0);
1248
+ if (remaining === 0) {
1249
+ truncated = true;
1250
+ break;
1251
+ }
1252
+ const raw = await backend.grep(pattern, "/", glob, remaining);
1173
1253
  if (raw.error) return raw;
1174
- const matches = (raw.matches || []).map((m) => ({
1254
+ for (const m of raw.matches || []) allMatches.push({
1175
1255
  ...m,
1176
1256
  path: routePrefix.slice(0, -1) + m.path
1177
- }));
1178
- allMatches.push(...matches);
1257
+ });
1258
+ truncated = truncated || raw.truncated === true;
1179
1259
  }
1180
- return { matches: allMatches };
1260
+ return applyGrepMaxCount({
1261
+ result: {
1262
+ matches: allMatches,
1263
+ truncated
1264
+ },
1265
+ maxCount
1266
+ });
1181
1267
  }
1182
1268
  /**
1183
1269
  * Structured glob matching returning FileInfo objects.
@@ -1188,26 +1274,33 @@ var CompositeBackend = class {
1188
1274
  const searchPath = path.substring(routePrefix.length - 1);
1189
1275
  const result = await backend.glob(pattern, searchPath || "/");
1190
1276
  if (result.error) return result;
1191
- return { files: (result.files || []).map((fi) => ({
1192
- ...fi,
1193
- path: routePrefix.slice(0, -1) + fi.path
1194
- })) };
1277
+ return {
1278
+ files: (result.files || []).map((fi) => ({
1279
+ ...fi,
1280
+ path: routePrefix.slice(0, -1) + fi.path
1281
+ })),
1282
+ truncated: result.truncated
1283
+ };
1195
1284
  }
1196
1285
  const defaultResult = await this.default.glob(pattern, path);
1197
1286
  if (defaultResult.error) return defaultResult;
1198
- results.push(...defaultResult.files || []);
1287
+ for (const fi of defaultResult.files || []) results.push(fi);
1288
+ let truncated = defaultResult.truncated === true;
1199
1289
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1200
1290
  if (!this.isRouteUnderPath(routePrefix, path)) continue;
1201
1291
  const result = await backend.glob(pattern, "/");
1202
1292
  if (result.error) continue;
1203
- const files = (result.files || []).map((fi) => ({
1293
+ for (const fi of result.files || []) results.push({
1204
1294
  ...fi,
1205
1295
  path: routePrefix.slice(0, -1) + fi.path
1206
- }));
1207
- results.push(...files);
1296
+ });
1297
+ truncated = truncated || result.truncated === true;
1208
1298
  }
1209
1299
  results.sort((a, b) => a.path.localeCompare(b.path));
1210
- return { files: results };
1300
+ return {
1301
+ files: results,
1302
+ truncated
1303
+ };
1211
1304
  }
1212
1305
  /**
1213
1306
  * Write content to a file, routing to appropriate backend.
@@ -1394,7 +1487,7 @@ const TOOLS_EXCLUDED_FROM_EVICTION = FILESYSTEM_TOOL_NAMES.filter((name) => name
1394
1487
  * Base64-encoded content is ~33% larger, so 10MB raw ≈ 13.3MB in context.
1395
1488
  * This keeps inline multimodal payloads within all major provider limits.
1396
1489
  */
1397
- const MAX_BINARY_READ_SIZE_BYTES = 10 * 1024 * 1024;
1490
+ const MAX_BINARY_READ_SIZE_BYTES = 10485760;
1398
1491
  /**
1399
1492
  * Template for truncation message in read_file.
1400
1493
  * {file_path} will be filled in at runtime.
@@ -1403,6 +1496,15 @@ const READ_FILE_TRUNCATION_MSG = `
1403
1496
 
1404
1497
  [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.]`;
1405
1498
  /**
1499
+ * Note appended to grep results that were cut short by the match-count cap.
1500
+ */
1501
+ 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.";
1502
+ /**
1503
+ * Default cap on the number of matches the grep tool returns.
1504
+ * Set to null to disable the cap.
1505
+ */
1506
+ const DEFAULT_GREP_MAX_COUNT = 1e3;
1507
+ /**
1406
1508
  * Message template for evicted tool results.
1407
1509
  */
1408
1510
  const TOO_LARGE_TOOL_MSG = langchain.context`
@@ -1474,8 +1576,9 @@ function buildEvictedHumanContent(message, replacementText) {
1474
1576
  */
1475
1577
  function buildTruncatedHumanMessage(message, filePath) {
1476
1578
  const contentSample = createContentPreview(extractTextFromMessage(message));
1579
+ const evictedContent = buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample));
1477
1580
  return new langchain.HumanMessage({
1478
- content: buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample)),
1581
+ content: evictedContent,
1479
1582
  id: message.id,
1480
1583
  additional_kwargs: { ...message.additional_kwargs },
1481
1584
  response_metadata: { ...message.response_metadata }
@@ -1749,7 +1852,7 @@ function createReadFileTool(backend, options) {
1749
1852
  const sizeBytes = Math.ceil(base64Data.length * 3 / 4);
1750
1853
  if (sizeBytes > 10485760) return [{
1751
1854
  type: "text",
1752
- text: `Error: file too large to read (${Math.round(sizeBytes / (1024 * 1024))}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / (1024 * 1024)}MB limit for binary files)`
1855
+ text: `Error: file too large to read (${Math.round(sizeBytes / 1048576)}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / 1048576}MB limit for binary files)`
1753
1856
  }];
1754
1857
  if (mimeType.startsWith("image/")) return [{
1755
1858
  type: "image",
@@ -1891,35 +1994,34 @@ function createGlobTool(backend, options) {
1891
1994
  * Create grep tool using backend.
1892
1995
  */
1893
1996
  function createGrepTool(backend, options) {
1894
- const { customDescription, permissions, includeExecution } = options;
1997
+ const { customDescription, permissions, includeExecution, grepMaxCount } = options;
1895
1998
  return (0, langchain.tool)(async (input, runtime) => {
1896
1999
  const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1897
2000
  if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
1898
2001
  const resolvedBackend = await resolveBackend(backend, runtime);
1899
- const { pattern, path = "/", glob = null } = input;
1900
- const result = await resolvedBackend.grep(pattern, path, glob);
2002
+ const { pattern, path = "/", glob = null, output_mode = "content" } = input;
2003
+ const maxCount = input.max_count ?? grepMaxCount;
2004
+ const result = await resolvedBackend.grep(pattern, path, glob, maxCount);
1901
2005
  if (result.error) return result.error;
1902
2006
  const matches = filterByPermissions(result.matches ?? [], permissions, "read", (m) => m.path);
1903
2007
  if (matches.length === 0) return `No matches found for pattern '${pattern}'`;
1904
- const lines = [];
1905
- let currentFile = null;
1906
- for (const match of matches) {
1907
- if (match.path !== currentFile) {
1908
- currentFile = match.path;
1909
- lines.push(`\n${currentFile}:`);
1910
- }
1911
- lines.push(` ${match.line}: ${match.text}`);
1912
- }
1913
- const truncated = truncateIfTooLong(lines);
1914
- if (Array.isArray(truncated)) return truncated.join("\n");
1915
- return truncated;
2008
+ const truncated = truncateIfTooLong(formatGrepMatches(matches, output_mode));
2009
+ let content = typeof truncated === "string" ? truncated : truncated.join("\n");
2010
+ if (result.truncated) content += `\n\n${GREP_TRUNCATION_NOTE}`;
2011
+ return content;
1916
2012
  }, {
1917
2013
  name: "grep",
1918
2014
  description: customDescription || getGrepToolDescription(includeExecution),
1919
2015
  schema: zod_v4.z.object({
1920
2016
  pattern: zod_v4.z.string().describe("Literal text pattern to search for (not regex)"),
1921
2017
  path: zod_v4.z.string().optional().default("/").describe("Base path to search from (default: /)"),
1922
- glob: zod_v4.z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')")
2018
+ glob: zod_v4.z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')"),
2019
+ max_count: zod_v4.z.number().int().positive().optional().nullable().default(null).describe("Optional cap on the total number of matches returned across all files. Leave unset to use the configured default. When the cap is hit, results are truncated and a note says so; narrow the pattern or path to see the rest."),
2020
+ output_mode: zod_v4.z.enum([
2021
+ "files_with_matches",
2022
+ "content",
2023
+ "count"
2024
+ ]).optional().default("content").describe("Output format: 'files_with_matches' lists matching file paths, 'content' shows matching lines (default), 'count' shows match counts per file")
1923
2025
  })
1924
2026
  });
1925
2027
  }
@@ -1988,7 +2090,7 @@ function allPathsScopedToRoutes(permissions, backend) {
1988
2090
  * ```
1989
2091
  */
1990
2092
  function createFilesystemMiddleware(options = {}) {
1991
- const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [], tools: filesystemTools = null } = options;
2093
+ const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [], tools: filesystemTools = null, grepMaxCount = DEFAULT_GREP_MAX_COUNT } = options;
1992
2094
  const enabledFilesystemTools = normalizeFilesystemTools(filesystemTools);
1993
2095
  const executeToolEnabled = enabledFilesystemTools == null || enabledFilesystemTools.has("execute");
1994
2096
  if (permissions.length > 0) validatePermissionPaths(permissions);
@@ -2024,7 +2126,8 @@ function createFilesystemMiddleware(options = {}) {
2024
2126
  grep: createGrepTool(backend, {
2025
2127
  customDescription: customToolDescriptions?.grep,
2026
2128
  permissions,
2027
- includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend)
2129
+ includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend),
2130
+ grepMaxCount
2028
2131
  }),
2029
2132
  execute: createExecuteTool(backend, {
2030
2133
  customDescription: customToolDescriptions?.execute,
@@ -2055,9 +2158,10 @@ function createFilesystemMiddleware(options = {}) {
2055
2158
  const evictPath = `/large_tool_results/${sanitizeToolCallId(fallbackToolCallId || msg.tool_call_id)}.txt`;
2056
2159
  const writeResult = await resolvedBackend.write(evictPath, textContent);
2057
2160
  const contentSample = createContentPreview(textContent);
2161
+ const replacementText = writeResult.error ? `Tool result too large, but the result could not be saved to the filesystem: ${writeResult.error}` : TOO_LARGE_TOOL_MSG.replace("{tool_call_id}", msg.tool_call_id).replace("{file_path}", evictPath).replace("{content_sample}", contentSample);
2058
2162
  return {
2059
2163
  message: new langchain.ToolMessage({
2060
- content: writeResult.error ? `Tool result too large, but the result could not be saved to the filesystem: ${writeResult.error}` : TOO_LARGE_TOOL_MSG.replace("{tool_call_id}", msg.tool_call_id).replace("{file_path}", evictPath).replace("{content_sample}", contentSample),
2164
+ content: replacementText,
2061
2165
  tool_call_id: msg.tool_call_id,
2062
2166
  name: msg.name,
2063
2167
  id: msg.id,
@@ -2402,6 +2506,7 @@ function createTaskTool(options) {
2402
2506
  }
2403
2507
  return subagentGraphs[subagentType];
2404
2508
  }
2509
+ const finalTaskDescription = taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions);
2405
2510
  return (0, langchain.tool)(async (input, config) => {
2406
2511
  const { description, subagent_type } = input;
2407
2512
  if (!(subagent_type in subagentGraphs)) {
@@ -2437,7 +2542,7 @@ function createTaskTool(options) {
2437
2542
  return returnCommandWithStateUpdate(result, config.toolCall.id);
2438
2543
  }, {
2439
2544
  name: "task",
2440
- description: taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions),
2545
+ description: finalTaskDescription,
2441
2546
  schema: zod_v4.z.object({
2442
2547
  description: zod_v4.z.string().describe("The task to execute with the selected agent"),
2443
2548
  subagent_type: zod_v4.z.string().describe(`Name of the agent to use. Available: ${Object.keys(subagentGraphs).join(", ")}`)
@@ -2449,18 +2554,19 @@ function createTaskTool(options) {
2449
2554
  */
2450
2555
  function createSubAgentMiddleware(options) {
2451
2556
  const { defaultModel, defaultTools = [], defaultMiddleware = null, generalPurposeMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = null, generalPurposeAgent = true, taskDescription = null } = options;
2557
+ const taskTool = createTaskTool({
2558
+ defaultModel,
2559
+ defaultTools,
2560
+ defaultMiddleware,
2561
+ generalPurposeMiddleware,
2562
+ defaultInterruptOn,
2563
+ subagents,
2564
+ generalPurposeAgent,
2565
+ taskDescription
2566
+ });
2452
2567
  return (0, langchain.createMiddleware)({
2453
2568
  name: "subAgentMiddleware",
2454
- tools: [createTaskTool({
2455
- defaultModel,
2456
- defaultTools,
2457
- defaultMiddleware,
2458
- generalPurposeMiddleware,
2459
- defaultInterruptOn,
2460
- subagents,
2461
- generalPurposeAgent,
2462
- taskDescription
2463
- })],
2569
+ tools: [taskTool],
2464
2570
  wrapModelCall: async (request, handler) => {
2465
2571
  if (systemPrompt !== null) return handler({
2466
2572
  ...request,
@@ -2965,7 +3071,7 @@ function createMemoryMiddleware(options) {
2965
3071
  * });
2966
3072
  * ```
2967
3073
  */
2968
- const MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024;
3074
+ const MAX_SKILL_FILE_SIZE = 10485760;
2969
3075
  const DEFAULT_SKILL_READ_LINE_LIMIT = 1e3;
2970
3076
  const MAX_SKILL_NAME_LENGTH = 64;
2971
3077
  const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
@@ -3733,7 +3839,6 @@ function createCompletionCallbackMiddleware(options) {
3733
3839
  * from `langchain` directly.
3734
3840
  */
3735
3841
  const DEFAULT_MESSAGES_TO_KEEP = 20;
3736
- const DEFAULT_TRIM_TOKEN_LIMIT = 4e3;
3737
3842
  const FALLBACK_TRIGGER = {
3738
3843
  type: "tokens",
3739
3844
  value: 17e4
@@ -3850,7 +3955,7 @@ function isSummaryMessage(msg) {
3850
3955
  * @returns AgentMiddleware for summarization and history offloading
3851
3956
  */
3852
3957
  function createSummarizationMiddleware(options) {
3853
- const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize = DEFAULT_TRIM_TOKEN_LIMIT, historyPathPrefix = "/conversation_history" } = options;
3958
+ const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize, historyPathPrefix = "/conversation_history" } = options;
3854
3959
  let trigger = options.trigger;
3855
3960
  let keep = options.keep ?? {
3856
3961
  type: "messages",
@@ -4054,7 +4159,9 @@ function createSummarizationMiddleware(options) {
4054
4159
  * This gives a more accurate picture of what actually gets sent to the model.
4055
4160
  */
4056
4161
  function countTotalTokens(messages, systemMessage, tools) {
4057
- return (0, langchain.countTokensApproximately)(systemMessage && langchain.SystemMessage.isInstance(systemMessage) ? [systemMessage, ...messages] : [...messages], tools && Array.isArray(tools) && tools.length > 0 ? tools : null);
4162
+ const countedMessages = systemMessage && langchain.SystemMessage.isInstance(systemMessage) ? [systemMessage, ...messages] : [...messages];
4163
+ const toolsArray = tools && Array.isArray(tools) && tools.length > 0 ? tools : null;
4164
+ return (0, langchain.countTokensApproximately)(countedMessages, toolsArray);
4058
4165
  }
4059
4166
  /**
4060
4167
  * Truncate ToolMessage content so that the total payload fits within the
@@ -4201,7 +4308,8 @@ function createSummarizationMiddleware(options) {
4201
4308
  */
4202
4309
  async function createSummary(messages, chatModel) {
4203
4310
  let messagesToSummarize = messages;
4204
- if ((0, langchain.countTokensApproximately)(messages) > trimTokensToSummarize) {
4311
+ const tokens = (0, langchain.countTokensApproximately)(messages);
4312
+ if (trimTokensToSummarize !== void 0 && tokens > trimTokensToSummarize) {
4205
4313
  let kept = 0;
4206
4314
  const trimmedMessages = [];
4207
4315
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -4214,8 +4322,7 @@ function createSummarizationMiddleware(options) {
4214
4322
  }
4215
4323
  const conversation = (0, _langchain_core_messages.getBufferString)(messagesToSummarize);
4216
4324
  const prompt = summaryPrompt.replace("{conversation}", conversation);
4217
- const response = await chatModel.invoke([new langchain.HumanMessage({ content: prompt })]);
4218
- return typeof response.content === "string" ? response.content : JSON.stringify(response.content);
4325
+ return (await chatModel.invoke([new langchain.HumanMessage({ content: prompt })])).text;
4219
4326
  }
4220
4327
  /**
4221
4328
  * Build the summary message with file path reference.
@@ -6386,7 +6493,7 @@ var StoreBackend = class {
6386
6493
  * Search file contents for a literal text pattern.
6387
6494
  * Binary files are skipped.
6388
6495
  */
6389
- async grep(pattern, path = "/", glob = null) {
6496
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
6390
6497
  const store = this.getStore();
6391
6498
  const namespace = this.getNamespace();
6392
6499
  const items = await this.searchStorePaginated(store, namespace);
@@ -6396,7 +6503,10 @@ var StoreBackend = class {
6396
6503
  } catch {
6397
6504
  continue;
6398
6505
  }
6399
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
6506
+ return applyGrepMaxCount({
6507
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
6508
+ maxCount
6509
+ });
6400
6510
  }
6401
6511
  /**
6402
6512
  * Structured glob matching returning FileInfo objects.
@@ -6695,7 +6805,7 @@ var ContextHubBackend = class ContextHubBackend {
6695
6805
  modified_at: now
6696
6806
  } };
6697
6807
  }
6698
- async grep(pattern, path = null, glob = null) {
6808
+ async grep(pattern, path = null, glob = null, maxCount = null) {
6699
6809
  let cache;
6700
6810
  try {
6701
6811
  cache = await this.ensureCache();
@@ -6718,7 +6828,10 @@ var ContextHubBackend = class ContextHubBackend {
6718
6828
  });
6719
6829
  }
6720
6830
  }
6721
- return { matches };
6831
+ return applyGrepMaxCount({
6832
+ result: { matches },
6833
+ maxCount
6834
+ });
6722
6835
  }
6723
6836
  async glob(pattern, _path = "/") {
6724
6837
  let cache;
@@ -7114,7 +7227,7 @@ var BaseSandbox = class {
7114
7227
  * @param glob - Optional glob pattern to filter which files to search.
7115
7228
  * @returns List of GrepMatch dicts containing path, line number, and matched text.
7116
7229
  */
7117
- async grep(pattern, path = "/", glob = null) {
7230
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
7118
7231
  const command = buildGrepCommand(pattern, path, glob);
7119
7232
  const output = (await this.execute(command)).output.trim();
7120
7233
  if (!output) return { matches: [] };
@@ -7132,7 +7245,10 @@ var BaseSandbox = class {
7132
7245
  });
7133
7246
  }
7134
7247
  }
7135
- return { matches };
7248
+ return applyGrepMaxCount({
7249
+ result: { matches },
7250
+ maxCount
7251
+ });
7136
7252
  }
7137
7253
  /**
7138
7254
  * Structured glob matching returning FileInfo objects.
@@ -7631,6 +7747,12 @@ Object.defineProperty(exports, "adaptSandboxProtocol", {
7631
7747
  return adaptSandboxProtocol;
7632
7748
  }
7633
7749
  });
7750
+ Object.defineProperty(exports, "applyGrepMaxCount", {
7751
+ enumerable: true,
7752
+ get: function() {
7753
+ return applyGrepMaxCount;
7754
+ }
7755
+ });
7634
7756
  Object.defineProperty(exports, "checkEmptyContent", {
7635
7757
  enumerable: true,
7636
7758
  get: function() {
@@ -7794,4 +7916,4 @@ Object.defineProperty(exports, "serializeProfile", {
7794
7916
  }
7795
7917
  });
7796
7918
 
7797
- //# sourceMappingURL=langsmith-DdOXam6Z.cjs.map
7919
+ //# sourceMappingURL=langsmith-Bjhs2iT_.cjs.map