deepagents 1.12.0 → 1.12.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.
@@ -342,11 +342,32 @@ function validatePath$1(path) {
342
342
  return normalized;
343
343
  }
344
344
  /**
345
+ * Resolve the files under `path` for grep/glob search.
346
+ *
347
+ * If `path` exactly names a file that exists in `files`, only that file is
348
+ * returned (exact match) — this lets grep/glob target a specific file
349
+ * directly instead of only matching directories. Otherwise `path` is treated
350
+ * as a directory and files are filtered by the normalized directory prefix.
351
+ *
352
+ * @returns Filtered files map, or null if `path` is invalid (e.g. whitespace-only).
353
+ */
354
+ function filterFilesByPath(files, path) {
355
+ const exactPath = path ? path.startsWith("/") ? path : "/" + path : "/";
356
+ if (Object.prototype.hasOwnProperty.call(files, exactPath)) return { [exactPath]: files[exactPath] };
357
+ try {
358
+ const normalizedPath = validatePath$1(path);
359
+ return Object.fromEntries(Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath)));
360
+ } catch {
361
+ return null;
362
+ }
363
+ }
364
+ /**
345
365
  * Search files dict for paths matching glob pattern.
346
366
  *
347
367
  * @param files - Dictionary of file paths to FileData
348
368
  * @param pattern - Glob pattern (e.g., `*.py`, `**\/*.ts`)
349
- * @param path - Base path to search from
369
+ * @param path - Base path to search from. If `path` names an exact file, only
370
+ * that file is considered.
350
371
  * @returns Newline-separated file paths, sorted by modification time (most recent first).
351
372
  * Returns "No files found" if no matches.
352
373
  *
@@ -358,13 +379,9 @@ function validatePath$1(path) {
358
379
  * ```
359
380
  */
360
381
  function globSearchFiles(files, pattern, path = "/") {
361
- let normalizedPath;
362
- try {
363
- normalizedPath = validatePath$1(path);
364
- } catch {
365
- return "No files found";
366
- }
367
- const filtered = Object.fromEntries(Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath)));
382
+ const filtered = filterFilesByPath(files, path);
383
+ if (filtered === null) return "No files found";
384
+ const normalizedPath = validatePath$1(path);
368
385
  const effectivePattern = pattern;
369
386
  const matches = [];
370
387
  for (const [filePath, fileData] of Object.entries(filtered)) {
@@ -387,16 +404,12 @@ function globSearchFiles(files, pattern, path = "/") {
387
404
  * Return structured grep matches from an in-memory files mapping.
388
405
  *
389
406
  * Performs literal text search (not regex). Binary files are skipped.
407
+ * If `path` names an exact file, only that file is considered.
390
408
  * Returns an empty array when no matches are found or on invalid input.
391
409
  */
392
410
  function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
393
- let normalizedPath;
394
- try {
395
- normalizedPath = validatePath$1(path);
396
- } catch {
397
- return [];
398
- }
399
- let filtered = Object.fromEntries(Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath)));
411
+ let filtered = filterFilesByPath(files, path);
412
+ if (filtered === null) return [];
400
413
  if (glob) filtered = Object.fromEntries(Object.entries(filtered).filter(([fp]) => micromatch.isMatch(basename(fp), glob, {
401
414
  dot: true,
402
415
  nobrace: false
@@ -517,9 +530,12 @@ function adaptBackendProtocol(backend) {
517
530
  if (typeof result === "string") return { content: result };
518
531
  return result;
519
532
  },
520
- async grep(pattern, path, glob) {
521
- const result = await ("grep" in backend ? backend.grep(pattern, path, glob) : backend.grepRaw(pattern, path, glob));
522
- if (Array.isArray(result)) return { matches: result };
533
+ async grep(pattern, path, glob, maxCount) {
534
+ const result = await ("grep" in backend ? backend.grep(pattern, path, glob, maxCount) : backend.grepRaw(pattern, path, glob));
535
+ if (Array.isArray(result)) return applyGrepMaxCount({
536
+ result: { matches: result },
537
+ maxCount
538
+ });
523
539
  if (typeof result === "string") return { error: result };
524
540
  return result;
525
541
  }
@@ -554,6 +570,21 @@ function adaptSandboxProtocol(sandbox) {
554
570
  //#endregion
555
571
  //#region src/backends/protocol.ts
556
572
  /**
573
+ * Enforce a match cap after a backend grep has completed.
574
+ *
575
+ * When `maxCount` is set and the result exceeds it, the matches are sliced
576
+ * to the cap and the result is flagged `truncated: true`.
577
+ */
578
+ function applyGrepMaxCount(params) {
579
+ const { result, maxCount } = params;
580
+ if (maxCount == null || result.matches == null || result.matches.length <= maxCount) return result;
581
+ return {
582
+ error: result.error,
583
+ matches: result.matches.slice(0, maxCount),
584
+ truncated: true
585
+ };
586
+ }
587
+ /**
557
588
  * Type guard to check if a backend supports execution.
558
589
  *
559
590
  * @param backend - Backend instance to check
@@ -834,9 +865,12 @@ var StateBackend = class {
834
865
  * Search file contents for a literal text pattern.
835
866
  * Binary files are skipped.
836
867
  */
837
- grep(pattern, path = "/", glob = null) {
868
+ grep(pattern, path = "/", glob = null, maxCount = null) {
838
869
  const files = this.files;
839
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
870
+ return applyGrepMaxCount({
871
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
872
+ maxCount
873
+ });
840
874
  }
841
875
  /**
842
876
  * Structured glob matching returning FileInfo objects.
@@ -1078,7 +1112,7 @@ var CompositeBackend = class {
1078
1112
  const results = [];
1079
1113
  const defaultResult = await this.default.ls(path);
1080
1114
  if (defaultResult.error) return defaultResult;
1081
- results.push(...defaultResult.files || []);
1115
+ for (const fi of defaultResult.files || []) results.push(fi);
1082
1116
  for (const [routePrefix] of this.sortedRoutes) results.push({
1083
1117
  path: routePrefix,
1084
1118
  is_dir: true,
@@ -1114,33 +1148,56 @@ var CompositeBackend = class {
1114
1148
  }
1115
1149
  /**
1116
1150
  * Structured search results or error string for invalid input.
1151
+ *
1152
+ * @param maxCount - Optional total cap on returned matches across all routed
1153
+ * backends. When the cap is reached, remaining routes are
1154
+ * short-circuited and the result is flagged `truncated: true`.
1117
1155
  */
1118
- async grep(pattern, path = "/", glob = null) {
1156
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
1119
1157
  const searchPath = path || "/";
1120
1158
  for (const [routePrefix, backend] of this.sortedRoutes) if (this.isPathWithinRoute(searchPath, routePrefix)) {
1121
1159
  const routeSearchPath = searchPath.substring(routePrefix.length - 1);
1122
- const raw = await backend.grep(pattern, routeSearchPath || "/", glob);
1160
+ const raw = await backend.grep(pattern, routeSearchPath || "/", glob, maxCount);
1123
1161
  if (raw.error) return raw;
1124
- return { matches: (raw.matches || []).map((m) => ({
1125
- ...m,
1126
- path: routePrefix.slice(0, -1) + m.path
1127
- })) };
1162
+ return applyGrepMaxCount({
1163
+ result: {
1164
+ matches: (raw.matches || []).map((m) => ({
1165
+ ...m,
1166
+ path: routePrefix.slice(0, -1) + m.path
1167
+ })),
1168
+ truncated: raw.truncated
1169
+ },
1170
+ maxCount
1171
+ });
1128
1172
  }
1129
1173
  const allMatches = [];
1130
- const rawDefault = await this.default.grep(pattern, searchPath, glob);
1174
+ let truncated = false;
1175
+ const rawDefault = await this.default.grep(pattern, searchPath, glob, maxCount);
1131
1176
  if (rawDefault.error) return rawDefault;
1132
- allMatches.push(...rawDefault.matches || []);
1177
+ for (const m of rawDefault.matches || []) allMatches.push(m);
1178
+ truncated = truncated || rawDefault.truncated === true;
1133
1179
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1134
1180
  if (!this.isRouteUnderPath(routePrefix, searchPath)) continue;
1135
- const raw = await backend.grep(pattern, "/", glob);
1181
+ const remaining = maxCount == null ? null : Math.max(maxCount - allMatches.length, 0);
1182
+ if (remaining === 0) {
1183
+ truncated = true;
1184
+ break;
1185
+ }
1186
+ const raw = await backend.grep(pattern, "/", glob, remaining);
1136
1187
  if (raw.error) return raw;
1137
- const matches = (raw.matches || []).map((m) => ({
1188
+ for (const m of raw.matches || []) allMatches.push({
1138
1189
  ...m,
1139
1190
  path: routePrefix.slice(0, -1) + m.path
1140
- }));
1141
- allMatches.push(...matches);
1191
+ });
1192
+ truncated = truncated || raw.truncated === true;
1142
1193
  }
1143
- return { matches: allMatches };
1194
+ return applyGrepMaxCount({
1195
+ result: {
1196
+ matches: allMatches,
1197
+ truncated
1198
+ },
1199
+ maxCount
1200
+ });
1144
1201
  }
1145
1202
  /**
1146
1203
  * Structured glob matching returning FileInfo objects.
@@ -1151,26 +1208,33 @@ var CompositeBackend = class {
1151
1208
  const searchPath = path.substring(routePrefix.length - 1);
1152
1209
  const result = await backend.glob(pattern, searchPath || "/");
1153
1210
  if (result.error) return result;
1154
- return { files: (result.files || []).map((fi) => ({
1155
- ...fi,
1156
- path: routePrefix.slice(0, -1) + fi.path
1157
- })) };
1211
+ return {
1212
+ files: (result.files || []).map((fi) => ({
1213
+ ...fi,
1214
+ path: routePrefix.slice(0, -1) + fi.path
1215
+ })),
1216
+ truncated: result.truncated
1217
+ };
1158
1218
  }
1159
1219
  const defaultResult = await this.default.glob(pattern, path);
1160
1220
  if (defaultResult.error) return defaultResult;
1161
- results.push(...defaultResult.files || []);
1221
+ for (const fi of defaultResult.files || []) results.push(fi);
1222
+ let truncated = defaultResult.truncated === true;
1162
1223
  for (const [routePrefix, backend] of Object.entries(this.routes)) {
1163
1224
  if (!this.isRouteUnderPath(routePrefix, path)) continue;
1164
1225
  const result = await backend.glob(pattern, "/");
1165
1226
  if (result.error) continue;
1166
- const files = (result.files || []).map((fi) => ({
1227
+ for (const fi of result.files || []) results.push({
1167
1228
  ...fi,
1168
1229
  path: routePrefix.slice(0, -1) + fi.path
1169
- }));
1170
- results.push(...files);
1230
+ });
1231
+ truncated = truncated || result.truncated === true;
1171
1232
  }
1172
1233
  results.sort((a, b) => a.path.localeCompare(b.path));
1173
- return { files: results };
1234
+ return {
1235
+ files: results,
1236
+ truncated
1237
+ };
1174
1238
  }
1175
1239
  /**
1176
1240
  * Write content to a file, routing to appropriate backend.
@@ -1366,6 +1430,15 @@ const READ_FILE_TRUNCATION_MSG = `
1366
1430
 
1367
1431
  [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.]`;
1368
1432
  /**
1433
+ * Note appended to grep results that were cut short by the match-count cap.
1434
+ */
1435
+ 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.";
1436
+ /**
1437
+ * Default cap on the number of matches the grep tool returns.
1438
+ * Set to null to disable the cap.
1439
+ */
1440
+ const DEFAULT_GREP_MAX_COUNT = 1e3;
1441
+ /**
1369
1442
  * Message template for evicted tool results.
1370
1443
  */
1371
1444
  const TOO_LARGE_TOOL_MSG = context`
@@ -1854,13 +1927,14 @@ function createGlobTool(backend, options) {
1854
1927
  * Create grep tool using backend.
1855
1928
  */
1856
1929
  function createGrepTool(backend, options) {
1857
- const { customDescription, permissions, includeExecution } = options;
1930
+ const { customDescription, permissions, includeExecution, grepMaxCount } = options;
1858
1931
  return tool(async (input, runtime) => {
1859
1932
  const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1860
1933
  if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
1861
1934
  const resolvedBackend = await resolveBackend(backend, runtime);
1862
1935
  const { pattern, path = "/", glob = null } = input;
1863
- const result = await resolvedBackend.grep(pattern, path, glob);
1936
+ const maxCount = input.max_count ?? grepMaxCount;
1937
+ const result = await resolvedBackend.grep(pattern, path, glob, maxCount);
1864
1938
  if (result.error) return result.error;
1865
1939
  const matches = filterByPermissions(result.matches ?? [], permissions, "read", (m) => m.path);
1866
1940
  if (matches.length === 0) return `No matches found for pattern '${pattern}'`;
@@ -1874,15 +1948,17 @@ function createGrepTool(backend, options) {
1874
1948
  lines.push(` ${match.line}: ${match.text}`);
1875
1949
  }
1876
1950
  const truncated = truncateIfTooLong(lines);
1877
- if (Array.isArray(truncated)) return truncated.join("\n");
1878
- return truncated;
1951
+ let content = Array.isArray(truncated) ? truncated.join("\n") : truncated;
1952
+ if (result.truncated) content += `\n\n${GREP_TRUNCATION_NOTE}`;
1953
+ return content;
1879
1954
  }, {
1880
1955
  name: "grep",
1881
1956
  description: customDescription || getGrepToolDescription(includeExecution),
1882
1957
  schema: z.object({
1883
1958
  pattern: z.string().describe("Literal text pattern to search for (not regex)"),
1884
1959
  path: z.string().optional().default("/").describe("Base path to search from (default: /)"),
1885
- glob: z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')")
1960
+ glob: z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')"),
1961
+ 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.")
1886
1962
  })
1887
1963
  });
1888
1964
  }
@@ -1951,7 +2027,7 @@ function allPathsScopedToRoutes(permissions, backend) {
1951
2027
  * ```
1952
2028
  */
1953
2029
  function createFilesystemMiddleware(options = {}) {
1954
- const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [], tools: filesystemTools = null } = options;
2030
+ 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;
1955
2031
  const enabledFilesystemTools = normalizeFilesystemTools(filesystemTools);
1956
2032
  const executeToolEnabled = enabledFilesystemTools == null || enabledFilesystemTools.has("execute");
1957
2033
  if (permissions.length > 0) validatePermissionPaths(permissions);
@@ -1987,7 +2063,8 @@ function createFilesystemMiddleware(options = {}) {
1987
2063
  grep: createGrepTool(backend, {
1988
2064
  customDescription: customToolDescriptions?.grep,
1989
2065
  permissions,
1990
- includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend)
2066
+ includeExecution: configuredToolNames.has("execute") && typeof backend !== "function" && isSandboxBackend(backend),
2067
+ grepMaxCount
1991
2068
  }),
1992
2069
  execute: createExecuteTool(backend, {
1993
2070
  customDescription: customToolDescriptions?.execute,
@@ -6344,7 +6421,7 @@ var StoreBackend = class {
6344
6421
  * Search file contents for a literal text pattern.
6345
6422
  * Binary files are skipped.
6346
6423
  */
6347
- async grep(pattern, path = "/", glob = null) {
6424
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
6348
6425
  const store = this.getStore();
6349
6426
  const namespace = this.getNamespace();
6350
6427
  const items = await this.searchStorePaginated(store, namespace);
@@ -6354,7 +6431,10 @@ var StoreBackend = class {
6354
6431
  } catch {
6355
6432
  continue;
6356
6433
  }
6357
- return { matches: grepMatchesFromFiles(files, pattern, path, glob) };
6434
+ return applyGrepMaxCount({
6435
+ result: { matches: grepMatchesFromFiles(files, pattern, path, glob) },
6436
+ maxCount
6437
+ });
6358
6438
  }
6359
6439
  /**
6360
6440
  * Structured glob matching returning FileInfo objects.
@@ -6653,7 +6733,7 @@ var ContextHubBackend = class ContextHubBackend {
6653
6733
  modified_at: now
6654
6734
  } };
6655
6735
  }
6656
- async grep(pattern, path = null, glob = null) {
6736
+ async grep(pattern, path = null, glob = null, maxCount = null) {
6657
6737
  let cache;
6658
6738
  try {
6659
6739
  cache = await this.ensureCache();
@@ -6676,7 +6756,10 @@ var ContextHubBackend = class ContextHubBackend {
6676
6756
  });
6677
6757
  }
6678
6758
  }
6679
- return { matches };
6759
+ return applyGrepMaxCount({
6760
+ result: { matches },
6761
+ maxCount
6762
+ });
6680
6763
  }
6681
6764
  async glob(pattern, _path = "/") {
6682
6765
  let cache;
@@ -7072,7 +7155,7 @@ var BaseSandbox = class {
7072
7155
  * @param glob - Optional glob pattern to filter which files to search.
7073
7156
  * @returns List of GrepMatch dicts containing path, line number, and matched text.
7074
7157
  */
7075
- async grep(pattern, path = "/", glob = null) {
7158
+ async grep(pattern, path = "/", glob = null, maxCount = null) {
7076
7159
  const command = buildGrepCommand(pattern, path, glob);
7077
7160
  const output = (await this.execute(command)).output.trim();
7078
7161
  if (!output) return { matches: [] };
@@ -7090,7 +7173,10 @@ var BaseSandbox = class {
7090
7173
  });
7091
7174
  }
7092
7175
  }
7093
- return { matches };
7176
+ return applyGrepMaxCount({
7177
+ result: { matches },
7178
+ maxCount
7179
+ });
7094
7180
  }
7095
7181
  /**
7096
7182
  * Structured glob matching returning FileInfo objects.
@@ -7445,6 +7531,6 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
7445
7531
  }
7446
7532
  };
7447
7533
  //#endregion
7448
- 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 };
7534
+ 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 };
7449
7535
 
7450
- //# sourceMappingURL=langsmith-hz83LfzA.js.map
7536
+ //# sourceMappingURL=langsmith-b3Dpu8rS.js.map