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.
@@ -302,13 +302,13 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
302
302
  function truncateIfTooLong(result) {
303
303
  if (Array.isArray(result)) {
304
304
  const totalChars = result.reduce((sum, item) => sum + item.length, 0);
305
- if (totalChars > 2e4 * 4) {
305
+ if (totalChars > 8e4) {
306
306
  const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
307
307
  return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
308
308
  }
309
309
  return result;
310
310
  }
311
- if (result.length > 2e4 * 4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
311
+ if (result.length > 8e4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
312
312
  return result;
313
313
  }
314
314
  /**
@@ -401,6 +401,30 @@ function globSearchFiles(files, pattern, path = "/") {
401
401
  return matches.map(([fp]) => fp).join("\n");
402
402
  }
403
403
  /**
404
+ * Format grep search results based on output mode.
405
+ *
406
+ * @param results - Dictionary mapping file paths to list of [line_num, line_content] tuples
407
+ * @param outputMode - Output format - "files_with_matches", "content", or "count"
408
+ * @returns Formatted string output
409
+ */
410
+ function formatGrepResults(results, outputMode) {
411
+ if (outputMode === "files_with_matches") return Object.keys(results).sort().join("\n");
412
+ if (outputMode === "count") {
413
+ const lines = [];
414
+ for (const filePath of Object.keys(results).sort()) {
415
+ const count = results[filePath].length;
416
+ lines.push(`${filePath}: ${count}`);
417
+ }
418
+ return lines.join("\n");
419
+ }
420
+ const lines = [];
421
+ for (const filePath of Object.keys(results).sort()) {
422
+ lines.push(`${filePath}:`);
423
+ for (const [lineNum, line] of results[filePath]) lines.push(` ${lineNum}: ${line}`);
424
+ }
425
+ return lines.join("\n");
426
+ }
427
+ /**
404
428
  * Return structured grep matches from an in-memory files mapping.
405
429
  *
406
430
  * Performs literal text search (not regex). Binary files are skipped.
@@ -431,6 +455,24 @@ function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
431
455
  return matches;
432
456
  }
433
457
  /**
458
+ * Group structured matches into the legacy dict form used by formatters.
459
+ */
460
+ function buildGrepResultsDict(matches) {
461
+ const grouped = {};
462
+ for (const m of matches) {
463
+ if (!grouped[m.path]) grouped[m.path] = [];
464
+ grouped[m.path].push([m.line, m.text]);
465
+ }
466
+ return grouped;
467
+ }
468
+ /**
469
+ * Format structured grep matches using existing formatting logic.
470
+ */
471
+ function formatGrepMatches(matches, outputMode) {
472
+ if (matches.length === 0) return "No matches found";
473
+ return formatGrepResults(buildGrepResultsDict(matches), outputMode);
474
+ }
475
+ /**
434
476
  * Determine MIME type from a file path's extension.
435
477
  *
436
478
  * Defaults to "text/plain" for unknown extensions. Only the known non-text
@@ -530,9 +572,12 @@ function adaptBackendProtocol(backend) {
530
572
  if (typeof result === "string") return { content: result };
531
573
  return result;
532
574
  },
533
- async grep(pattern, path, glob) {
534
- const result = await ("grep" in backend ? backend.grep(pattern, path, glob) : backend.grepRaw(pattern, path, glob));
535
- if (Array.isArray(result)) return { matches: result };
575
+ async grep(pattern, path, glob, maxCount) {
576
+ const result = await ("grep" in backend ? backend.grep(pattern, path, glob, maxCount) : backend.grepRaw(pattern, path, glob));
577
+ if (Array.isArray(result)) return applyGrepMaxCount({
578
+ result: { matches: result },
579
+ maxCount
580
+ });
536
581
  if (typeof result === "string") return { error: result };
537
582
  return result;
538
583
  }
@@ -567,6 +612,21 @@ function adaptSandboxProtocol(sandbox) {
567
612
  //#endregion
568
613
  //#region src/backends/protocol.ts
569
614
  /**
615
+ * Enforce a match cap after a backend grep has completed.
616
+ *
617
+ * When `maxCount` is set and the result exceeds it, the matches are sliced
618
+ * to the cap and the result is flagged `truncated: true`.
619
+ */
620
+ function applyGrepMaxCount(params) {
621
+ const { result, maxCount } = params;
622
+ if (maxCount == null || result.matches == null || result.matches.length <= maxCount) return result;
623
+ return {
624
+ error: result.error,
625
+ matches: result.matches.slice(0, maxCount),
626
+ truncated: true
627
+ };
628
+ }
629
+ /**
570
630
  * Type guard to check if a backend supports execution.
571
631
  *
572
632
  * @param backend - Backend instance to check
@@ -847,9 +907,12 @@ var StateBackend = class {
847
907
  * Search file contents for a literal text pattern.
848
908
  * Binary files are skipped.
849
909
  */
850
- grep(pattern, path = "/", glob = null) {
910
+ grep(pattern, path = "/", glob = null, maxCount = null) {
851
911
  const files = this.files;
852
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
912
+ return applyGrepMaxCount({
913
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
914
+ maxCount
915
+ });
853
916
  }
854
917
  /**
855
918
  * Structured glob matching returning FileInfo objects.
@@ -1091,7 +1154,7 @@ var CompositeBackend = class {
1091
1154
  const results = [];
1092
1155
  const defaultResult = await this.default.ls(path);
1093
1156
  if (defaultResult.error) return defaultResult;
1094
- results.push(...defaultResult.files || []);
1157
+ for (const fi of defaultResult.files || []) results.push(fi);
1095
1158
  for (const [routePrefix] of this.sortedRoutes) results.push({
1096
1159
  path: routePrefix,
1097
1160
  is_dir: true,
@@ -1127,33 +1190,56 @@ var CompositeBackend = class {
1127
1190
  }
1128
1191
  /**
1129
1192
  * Structured search results or error string for invalid input.
1193
+ *
1194
+ * @param maxCount - Optional total cap on returned matches across all routed
1195
+ * backends. When the cap is reached, remaining routes are
1196
+ * short-circuited and the result is flagged `truncated: true`.
1130
1197
  */
1131
- async grep(pattern, path = "/", glob = null) {
1198
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
1132
1199
  const searchPath = path || "/";
1133
1200
  for (const [routePrefix, backend] of this.sortedRoutes) if (this.isPathWithinRoute(searchPath, routePrefix)) {
1134
1201
  const routeSearchPath = searchPath.substring(routePrefix.length - 1);
1135
- const raw = await backend.grep(pattern, routeSearchPath || "/", glob);
1202
+ const raw = await backend.grep(pattern, routeSearchPath || "/", glob, maxCount);
1136
1203
  if (raw.error) return raw;
1137
- return { matches: (raw.matches || []).map((m) => ({
1138
- ...m,
1139
- path: routePrefix.slice(0, -1) + m.path
1140
- })) };
1204
+ return applyGrepMaxCount({
1205
+ result: {
1206
+ matches: (raw.matches || []).map((m) => ({
1207
+ ...m,
1208
+ path: routePrefix.slice(0, -1) + m.path
1209
+ })),
1210
+ truncated: raw.truncated
1211
+ },
1212
+ maxCount
1213
+ });
1141
1214
  }
1142
1215
  const allMatches = [];
1143
- const rawDefault = await this.default.grep(pattern, searchPath, glob);
1216
+ let truncated = false;
1217
+ const rawDefault = await this.default.grep(pattern, searchPath, glob, maxCount);
1144
1218
  if (rawDefault.error) return rawDefault;
1145
- allMatches.push(...rawDefault.matches || []);
1219
+ for (const m of rawDefault.matches || []) allMatches.push(m);
1220
+ truncated = truncated || rawDefault.truncated === true;
1146
1221
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1147
1222
  if (!this.isRouteUnderPath(routePrefix, searchPath)) continue;
1148
- const raw = await backend.grep(pattern, "/", glob);
1223
+ const remaining = maxCount == null ? null : Math.max(maxCount - allMatches.length, 0);
1224
+ if (remaining === 0) {
1225
+ truncated = true;
1226
+ break;
1227
+ }
1228
+ const raw = await backend.grep(pattern, "/", glob, remaining);
1149
1229
  if (raw.error) return raw;
1150
- const matches = (raw.matches || []).map((m) => ({
1230
+ for (const m of raw.matches || []) allMatches.push({
1151
1231
  ...m,
1152
1232
  path: routePrefix.slice(0, -1) + m.path
1153
- }));
1154
- allMatches.push(...matches);
1233
+ });
1234
+ truncated = truncated || raw.truncated === true;
1155
1235
  }
1156
- return { matches: allMatches };
1236
+ return applyGrepMaxCount({
1237
+ result: {
1238
+ matches: allMatches,
1239
+ truncated
1240
+ },
1241
+ maxCount
1242
+ });
1157
1243
  }
1158
1244
  /**
1159
1245
  * Structured glob matching returning FileInfo objects.
@@ -1164,26 +1250,33 @@ var CompositeBackend = class {
1164
1250
  const searchPath = path.substring(routePrefix.length - 1);
1165
1251
  const result = await backend.glob(pattern, searchPath || "/");
1166
1252
  if (result.error) return result;
1167
- return { files: (result.files || []).map((fi) => ({
1168
- ...fi,
1169
- path: routePrefix.slice(0, -1) + fi.path
1170
- })) };
1253
+ return {
1254
+ files: (result.files || []).map((fi) => ({
1255
+ ...fi,
1256
+ path: routePrefix.slice(0, -1) + fi.path
1257
+ })),
1258
+ truncated: result.truncated
1259
+ };
1171
1260
  }
1172
1261
  const defaultResult = await this.default.glob(pattern, path);
1173
1262
  if (defaultResult.error) return defaultResult;
1174
- results.push(...defaultResult.files || []);
1263
+ for (const fi of defaultResult.files || []) results.push(fi);
1264
+ let truncated = defaultResult.truncated === true;
1175
1265
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1176
1266
  if (!this.isRouteUnderPath(routePrefix, path)) continue;
1177
1267
  const result = await backend.glob(pattern, "/");
1178
1268
  if (result.error) continue;
1179
- const files = (result.files || []).map((fi) => ({
1269
+ for (const fi of result.files || []) results.push({
1180
1270
  ...fi,
1181
1271
  path: routePrefix.slice(0, -1) + fi.path
1182
- }));
1183
- results.push(...files);
1272
+ });
1273
+ truncated = truncated || result.truncated === true;
1184
1274
  }
1185
1275
  results.sort((a, b) => a.path.localeCompare(b.path));
1186
- return { files: results };
1276
+ return {
1277
+ files: results,
1278
+ truncated
1279
+ };
1187
1280
  }
1188
1281
  /**
1189
1282
  * Write content to a file, routing to appropriate backend.
@@ -1370,7 +1463,7 @@ const TOOLS_EXCLUDED_FROM_EVICTION = FILESYSTEM_TOOL_NAMES.filter((name) => name
1370
1463
  * Base64-encoded content is ~33% larger, so 10MB raw ≈ 13.3MB in context.
1371
1464
  * This keeps inline multimodal payloads within all major provider limits.
1372
1465
  */
1373
- const MAX_BINARY_READ_SIZE_BYTES = 10 * 1024 * 1024;
1466
+ const MAX_BINARY_READ_SIZE_BYTES = 10485760;
1374
1467
  /**
1375
1468
  * Template for truncation message in read_file.
1376
1469
  * {file_path} will be filled in at runtime.
@@ -1379,6 +1472,15 @@ const READ_FILE_TRUNCATION_MSG = `
1379
1472
 
1380
1473
  [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.]`;
1381
1474
  /**
1475
+ * Note appended to grep results that were cut short by the match-count cap.
1476
+ */
1477
+ 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.";
1478
+ /**
1479
+ * Default cap on the number of matches the grep tool returns.
1480
+ * Set to null to disable the cap.
1481
+ */
1482
+ const DEFAULT_GREP_MAX_COUNT = 1e3;
1483
+ /**
1382
1484
  * Message template for evicted tool results.
1383
1485
  */
1384
1486
  const TOO_LARGE_TOOL_MSG = context`
@@ -1450,8 +1552,9 @@ function buildEvictedHumanContent(message, replacementText) {
1450
1552
  */
1451
1553
  function buildTruncatedHumanMessage(message, filePath) {
1452
1554
  const contentSample = createContentPreview(extractTextFromMessage(message));
1555
+ const evictedContent = buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample));
1453
1556
  return new HumanMessage({
1454
- content: buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample)),
1557
+ content: evictedContent,
1455
1558
  id: message.id,
1456
1559
  additional_kwargs: { ...message.additional_kwargs },
1457
1560
  response_metadata: { ...message.response_metadata }
@@ -1725,7 +1828,7 @@ function createReadFileTool(backend, options) {
1725
1828
  const sizeBytes = Math.ceil(base64Data.length * 3 / 4);
1726
1829
  if (sizeBytes > 10485760) return [{
1727
1830
  type: "text",
1728
- 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)`
1831
+ text: `Error: file too large to read (${Math.round(sizeBytes / 1048576)}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / 1048576}MB limit for binary files)`
1729
1832
  }];
1730
1833
  if (mimeType.startsWith("image/")) return [{
1731
1834
  type: "image",
@@ -1867,35 +1970,34 @@ function createGlobTool(backend, options) {
1867
1970
  * Create grep tool using backend.
1868
1971
  */
1869
1972
  function createGrepTool(backend, options) {
1870
- const { customDescription, permissions, includeExecution } = options;
1973
+ const { customDescription, permissions, includeExecution, grepMaxCount } = options;
1871
1974
  return tool(async (input, runtime) => {
1872
1975
  const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1873
1976
  if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
1874
1977
  const resolvedBackend = await resolveBackend(backend, runtime);
1875
- const { pattern, path = "/", glob = null } = input;
1876
- const result = await resolvedBackend.grep(pattern, path, glob);
1978
+ const { pattern, path = "/", glob = null, output_mode = "content" } = input;
1979
+ const maxCount = input.max_count ?? grepMaxCount;
1980
+ const result = await resolvedBackend.grep(pattern, path, glob, maxCount);
1877
1981
  if (result.error) return result.error;
1878
1982
  const matches = filterByPermissions(result.matches ?? [], permissions, "read", (m) => m.path);
1879
1983
  if (matches.length === 0) return `No matches found for pattern '${pattern}'`;
1880
- const lines = [];
1881
- let currentFile = null;
1882
- for (const match of matches) {
1883
- if (match.path !== currentFile) {
1884
- currentFile = match.path;
1885
- lines.push(`\n${currentFile}:`);
1886
- }
1887
- lines.push(` ${match.line}: ${match.text}`);
1888
- }
1889
- const truncated = truncateIfTooLong(lines);
1890
- if (Array.isArray(truncated)) return truncated.join("\n");
1891
- return truncated;
1984
+ const truncated = truncateIfTooLong(formatGrepMatches(matches, output_mode));
1985
+ let content = typeof truncated === "string" ? truncated : truncated.join("\n");
1986
+ if (result.truncated) content += `\n\n${GREP_TRUNCATION_NOTE}`;
1987
+ return content;
1892
1988
  }, {
1893
1989
  name: "grep",
1894
1990
  description: customDescription || getGrepToolDescription(includeExecution),
1895
1991
  schema: z.object({
1896
1992
  pattern: z.string().describe("Literal text pattern to search for (not regex)"),
1897
1993
  path: z.string().optional().default("/").describe("Base path to search from (default: /)"),
1898
- glob: z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')")
1994
+ glob: z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')"),
1995
+ max_count: 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."),
1996
+ output_mode: z.enum([
1997
+ "files_with_matches",
1998
+ "content",
1999
+ "count"
2000
+ ]).optional().default("content").describe("Output format: 'files_with_matches' lists matching file paths, 'content' shows matching lines (default), 'count' shows match counts per file")
1899
2001
  })
1900
2002
  });
1901
2003
  }
@@ -1964,7 +2066,7 @@ function allPathsScopedToRoutes(permissions, backend) {
1964
2066
  * ```
1965
2067
  */
1966
2068
  function createFilesystemMiddleware(options = {}) {
1967
- const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [], tools: filesystemTools = null } = options;
2069
+ 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;
1968
2070
  const enabledFilesystemTools = normalizeFilesystemTools(filesystemTools);
1969
2071
  const executeToolEnabled = enabledFilesystemTools == null || enabledFilesystemTools.has("execute");
1970
2072
  if (permissions.length > 0) validatePermissionPaths(permissions);
@@ -2000,7 +2102,8 @@ function createFilesystemMiddleware(options = {}) {
2000
2102
  grep: createGrepTool(backend, {
2001
2103
  customDescription: customToolDescriptions?.grep,
2002
2104
  permissions,
2003
- includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend)
2105
+ includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend),
2106
+ grepMaxCount
2004
2107
  }),
2005
2108
  execute: createExecuteTool(backend, {
2006
2109
  customDescription: customToolDescriptions?.execute,
@@ -2031,9 +2134,10 @@ function createFilesystemMiddleware(options = {}) {
2031
2134
  const evictPath = `/large_tool_results/${sanitizeToolCallId(fallbackToolCallId || msg.tool_call_id)}.txt`;
2032
2135
  const writeResult = await resolvedBackend.write(evictPath, textContent);
2033
2136
  const contentSample = createContentPreview(textContent);
2137
+ 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);
2034
2138
  return {
2035
2139
  message: new ToolMessage({
2036
- 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),
2140
+ content: replacementText,
2037
2141
  tool_call_id: msg.tool_call_id,
2038
2142
  name: msg.name,
2039
2143
  id: msg.id,
@@ -2378,6 +2482,7 @@ function createTaskTool(options) {
2378
2482
  }
2379
2483
  return subagentGraphs[subagentType];
2380
2484
  }
2485
+ const finalTaskDescription = taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions);
2381
2486
  return tool(async (input, config) => {
2382
2487
  const { description, subagent_type } = input;
2383
2488
  if (!(subagent_type in subagentGraphs)) {
@@ -2413,7 +2518,7 @@ function createTaskTool(options) {
2413
2518
  return returnCommandWithStateUpdate(result, config.toolCall.id);
2414
2519
  }, {
2415
2520
  name: "task",
2416
- description: taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions),
2521
+ description: finalTaskDescription,
2417
2522
  schema: z.object({
2418
2523
  description: z.string().describe("The task to execute with the selected agent"),
2419
2524
  subagent_type: z.string().describe(`Name of the agent to use. Available: ${Object.keys(subagentGraphs).join(", ")}`)
@@ -2425,18 +2530,19 @@ function createTaskTool(options) {
2425
2530
  */
2426
2531
  function createSubAgentMiddleware(options) {
2427
2532
  const { defaultModel, defaultTools = [], defaultMiddleware = null, generalPurposeMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = null, generalPurposeAgent = true, taskDescription = null } = options;
2533
+ const taskTool = createTaskTool({
2534
+ defaultModel,
2535
+ defaultTools,
2536
+ defaultMiddleware,
2537
+ generalPurposeMiddleware,
2538
+ defaultInterruptOn,
2539
+ subagents,
2540
+ generalPurposeAgent,
2541
+ taskDescription
2542
+ });
2428
2543
  return createMiddleware({
2429
2544
  name: "subAgentMiddleware",
2430
- tools: [createTaskTool({
2431
- defaultModel,
2432
- defaultTools,
2433
- defaultMiddleware,
2434
- generalPurposeMiddleware,
2435
- defaultInterruptOn,
2436
- subagents,
2437
- generalPurposeAgent,
2438
- taskDescription
2439
- })],
2545
+ tools: [taskTool],
2440
2546
  wrapModelCall: async (request, handler) => {
2441
2547
  if (systemPrompt !== null) return handler({
2442
2548
  ...request,
@@ -2941,7 +3047,7 @@ function createMemoryMiddleware(options) {
2941
3047
  * });
2942
3048
  * ```
2943
3049
  */
2944
- const MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024;
3050
+ const MAX_SKILL_FILE_SIZE = 10485760;
2945
3051
  const DEFAULT_SKILL_READ_LINE_LIMIT = 1e3;
2946
3052
  const MAX_SKILL_NAME_LENGTH = 64;
2947
3053
  const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
@@ -3704,7 +3810,6 @@ function createCompletionCallbackMiddleware(options) {
3704
3810
  * from `langchain` directly.
3705
3811
  */
3706
3812
  const DEFAULT_MESSAGES_TO_KEEP = 20;
3707
- const DEFAULT_TRIM_TOKEN_LIMIT = 4e3;
3708
3813
  const FALLBACK_TRIGGER = {
3709
3814
  type: "tokens",
3710
3815
  value: 17e4
@@ -3821,7 +3926,7 @@ function isSummaryMessage(msg) {
3821
3926
  * @returns AgentMiddleware for summarization and history offloading
3822
3927
  */
3823
3928
  function createSummarizationMiddleware(options) {
3824
- const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize = DEFAULT_TRIM_TOKEN_LIMIT, historyPathPrefix = "/conversation_history" } = options;
3929
+ const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize, historyPathPrefix = "/conversation_history" } = options;
3825
3930
  let trigger = options.trigger;
3826
3931
  let keep = options.keep ?? {
3827
3932
  type: "messages",
@@ -4025,7 +4130,9 @@ function createSummarizationMiddleware(options) {
4025
4130
  * This gives a more accurate picture of what actually gets sent to the model.
4026
4131
  */
4027
4132
  function countTotalTokens(messages, systemMessage, tools) {
4028
- return countTokensApproximately(systemMessage && SystemMessage.isInstance(systemMessage) ? [systemMessage, ...messages] : [...messages], tools && Array.isArray(tools) && tools.length > 0 ? tools : null);
4133
+ const countedMessages = systemMessage && SystemMessage.isInstance(systemMessage) ? [systemMessage, ...messages] : [...messages];
4134
+ const toolsArray = tools && Array.isArray(tools) && tools.length > 0 ? tools : null;
4135
+ return countTokensApproximately(countedMessages, toolsArray);
4029
4136
  }
4030
4137
  /**
4031
4138
  * Truncate ToolMessage content so that the total payload fits within the
@@ -4172,7 +4279,8 @@ function createSummarizationMiddleware(options) {
4172
4279
  */
4173
4280
  async function createSummary(messages, chatModel) {
4174
4281
  let messagesToSummarize = messages;
4175
- if (countTokensApproximately(messages) > trimTokensToSummarize) {
4282
+ const tokens = countTokensApproximately(messages);
4283
+ if (trimTokensToSummarize !== void 0 && tokens > trimTokensToSummarize) {
4176
4284
  let kept = 0;
4177
4285
  const trimmedMessages = [];
4178
4286
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -4185,8 +4293,7 @@ function createSummarizationMiddleware(options) {
4185
4293
  }
4186
4294
  const conversation = getBufferString(messagesToSummarize);
4187
4295
  const prompt = summaryPrompt.replace("{conversation}", conversation);
4188
- const response = await chatModel.invoke([new HumanMessage({ content: prompt })]);
4189
- return typeof response.content === "string" ? response.content : JSON.stringify(response.content);
4296
+ return (await chatModel.invoke([new HumanMessage({ content: prompt })])).text;
4190
4297
  }
4191
4298
  /**
4192
4299
  * Build the summary message with file path reference.
@@ -6357,7 +6464,7 @@ var StoreBackend = class {
6357
6464
  * Search file contents for a literal text pattern.
6358
6465
  * Binary files are skipped.
6359
6466
  */
6360
- async grep(pattern, path = "/", glob = null) {
6467
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
6361
6468
  const store = this.getStore();
6362
6469
  const namespace = this.getNamespace();
6363
6470
  const items = await this.searchStorePaginated(store, namespace);
@@ -6367,7 +6474,10 @@ var StoreBackend = class {
6367
6474
  } catch {
6368
6475
  continue;
6369
6476
  }
6370
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
6477
+ return applyGrepMaxCount({
6478
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
6479
+ maxCount
6480
+ });
6371
6481
  }
6372
6482
  /**
6373
6483
  * Structured glob matching returning FileInfo objects.
@@ -6666,7 +6776,7 @@ var ContextHubBackend = class ContextHubBackend {
6666
6776
  modified_at: now
6667
6777
  } };
6668
6778
  }
6669
- async grep(pattern, path = null, glob = null) {
6779
+ async grep(pattern, path = null, glob = null, maxCount = null) {
6670
6780
  let cache;
6671
6781
  try {
6672
6782
  cache = await this.ensureCache();
@@ -6689,7 +6799,10 @@ var ContextHubBackend = class ContextHubBackend {
6689
6799
  });
6690
6800
  }
6691
6801
  }
6692
- return { matches };
6802
+ return applyGrepMaxCount({
6803
+ result: { matches },
6804
+ maxCount
6805
+ });
6693
6806
  }
6694
6807
  async glob(pattern, _path = "/") {
6695
6808
  let cache;
@@ -7085,7 +7198,7 @@ var BaseSandbox = class {
7085
7198
  * @param glob - Optional glob pattern to filter which files to search.
7086
7199
  * @returns List of GrepMatch dicts containing path, line number, and matched text.
7087
7200
  */
7088
- async grep(pattern, path = "/", glob = null) {
7201
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
7089
7202
  const command = buildGrepCommand(pattern, path, glob);
7090
7203
  const output = (await this.execute(command)).output.trim();
7091
7204
  if (!output) return { matches: [] };
@@ -7103,7 +7216,10 @@ var BaseSandbox = class {
7103
7216
  });
7104
7217
  }
7105
7218
  }
7106
- return { matches };
7219
+ return applyGrepMaxCount({
7220
+ result: { matches },
7221
+ maxCount
7222
+ });
7107
7223
  }
7108
7224
  /**
7109
7225
  * Structured glob matching returning FileInfo objects.
@@ -7458,6 +7574,6 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
7458
7574
  }
7459
7575
  };
7460
7576
  //#endregion
7461
- export { filesValue as A, StateBackend as B, createSummarizationMiddleware as C, MAX_SKILL_NAME_LENGTH as D, MAX_SKILL_FILE_SIZE as E, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as F, adaptBackendProtocol as G, isSandboxBackend as H, createSubAgent as I, getMimeType as J, adaptSandboxProtocol as K, createSubAgentMiddleware as L, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as M, DEFAULT_SUBAGENT_PROMPT as N, createSkillsMiddleware as O, GENERAL_PURPOSE_SUBAGENT as P, createFilesystemMiddleware as R, computeSummarizationDefaults as S, MAX_SKILL_DESCRIPTION_LENGTH as T, isSandboxProtocol as U, SandboxError as V, resolveBackend as W, performStringReplacement as X, isTextMimeType as Y, createHarnessProfile as _, ASYNC_TASK_SYSTEM_PROMPT as a, createAsyncSubAgentMiddleware as b, TASK_SYSTEM_PROMPT as c, registerHarnessProfile as d, generalPurposeSubagentConfigSchema as f, EMPTY_HARNESS_PROFILE as g, serializeProfile as h, StoreBackend as i, createPatchToolCallsMiddleware as j, createMemoryMiddleware as k, createDeepAgent as l, parseHarnessProfileConfig as m, BaseSandbox as n, BASE_AGENT_PROMPT as o, harnessProfileConfigSchema as p, checkEmptyContent as q, ContextHubBackend as r, EXECUTION_SYSTEM_PROMPT as s, LangSmithSandbox as t, getHarnessProfile as u, REQUIRED_MIDDLEWARE_NAMES as v, createCompletionCallbackMiddleware as w, isAsyncSubAgent as x, ConfigurationError as y, CompositeBackend as z };
7577
+ export { filesValue as A, StateBackend as B, createSummarizationMiddleware as C, MAX_SKILL_NAME_LENGTH as D, MAX_SKILL_FILE_SIZE as E, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as F, resolveBackend as G, applyGrepMaxCount as H, createSubAgent as I, checkEmptyContent as J, adaptBackendProtocol as K, createSubAgentMiddleware as L, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as M, DEFAULT_SUBAGENT_PROMPT as N, createSkillsMiddleware as O, GENERAL_PURPOSE_SUBAGENT as P, createFilesystemMiddleware as R, computeSummarizationDefaults as S, MAX_SKILL_DESCRIPTION_LENGTH as T, isSandboxBackend as U, SandboxError as V, isSandboxProtocol as W, isTextMimeType as X, getMimeType as Y, performStringReplacement as Z, createHarnessProfile as _, ASYNC_TASK_SYSTEM_PROMPT as a, createAsyncSubAgentMiddleware as b, TASK_SYSTEM_PROMPT as c, registerHarnessProfile as d, generalPurposeSubagentConfigSchema as f, EMPTY_HARNESS_PROFILE as g, serializeProfile as h, StoreBackend as i, createPatchToolCallsMiddleware as j, createMemoryMiddleware as k, createDeepAgent as l, parseHarnessProfileConfig as m, BaseSandbox as n, BASE_AGENT_PROMPT as o, harnessProfileConfigSchema as p, adaptSandboxProtocol as q, ContextHubBackend as r, EXECUTION_SYSTEM_PROMPT as s, LangSmithSandbox as t, getHarnessProfile as u, REQUIRED_MIDDLEWARE_NAMES as v, createCompletionCallbackMiddleware as w, isAsyncSubAgent as x, ConfigurationError as y, CompositeBackend as z };
7462
7578
 
7463
- //# sourceMappingURL=langsmith-DgbmWtWj.js.map
7579
+ //# sourceMappingURL=langsmith-CUTUAjHo.js.map