deepagents 1.10.8 → 1.11.1

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.
@@ -470,7 +470,7 @@ function migrateToFileDataV2(data, filePath) {
470
470
  * @returns BackendProtocolV2-compatible backend
471
471
  */
472
472
  function adaptBackendProtocol(backend) {
473
- return {
473
+ const adapted = {
474
474
  async ls(path) {
475
475
  const result = await ("ls" in backend ? backend.ls(path) : backend.lsInfo(path));
476
476
  if (Array.isArray(result)) return { files: result };
@@ -488,6 +488,7 @@ function adaptBackendProtocol(backend) {
488
488
  },
489
489
  write: (filePath, content) => backend.write(filePath, content),
490
490
  edit: (filePath, oldString, newString, replaceAll) => backend.edit(filePath, oldString, newString, replaceAll),
491
+ delete: backend.delete?.bind(backend),
491
492
  uploadFiles: backend.uploadFiles ? (files) => backend.uploadFiles(files) : void 0,
492
493
  downloadFiles: backend.downloadFiles ? (paths) => backend.downloadFiles(paths) : void 0,
493
494
  async read(filePath, offset, limit) {
@@ -502,6 +503,13 @@ function adaptBackendProtocol(backend) {
502
503
  return result;
503
504
  }
504
505
  };
506
+ const routePrefixes = backend.routePrefixes;
507
+ if (Array.isArray(routePrefixes)) Object.defineProperty(adapted, "routePrefixes", {
508
+ value: routePrefixes,
509
+ enumerable: true,
510
+ configurable: true
511
+ });
512
+ return adapted;
505
513
  }
506
514
  /**
507
515
  * Adapt a sandbox backend from v1 to v2 interface.
@@ -669,7 +677,8 @@ var StateBackend = class {
669
677
  * In legacy mode, this is a no-op — the caller uses `filesUpdate`
670
678
  * from the return value instead.
671
679
  *
672
- * @param update - Map of file paths to their updated {@link FileData}
680
+ * @param update - Map of file paths to their updated {@link FileData},
681
+ * or null deletion markers.
673
682
  */
674
683
  sendFilesUpdate(update) {
675
684
  if (this.isLegacy) return;
@@ -793,6 +802,15 @@ var StateBackend = class {
793
802
  };
794
803
  }
795
804
  /**
805
+ * Delete a file from state by sending a null deletion marker through Pregel.
806
+ */
807
+ delete(filePath) {
808
+ if (!(filePath in this.files)) return { error: `Error: File '${filePath}' not found` };
809
+ if (this.isLegacy) return { error: "StateBackend.delete requires a zero-argument StateBackend in a LangGraph execution context." };
810
+ this.sendFilesUpdate({ [filePath]: null });
811
+ return { path: filePath };
812
+ }
813
+ /**
796
814
  * Search file contents for a literal text pattern.
797
815
  * Binary files are skipped.
798
816
  */
@@ -1159,6 +1177,19 @@ var CompositeBackend = class {
1159
1177
  return await backend.edit(strippedKey, oldString, newString, replaceAll);
1160
1178
  }
1161
1179
  /**
1180
+ * Delete a file, routing to the appropriate backend.
1181
+ */
1182
+ async delete(filePath) {
1183
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
1184
+ if (!backend.delete) return { error: "Backend does not support delete" };
1185
+ const result = await backend.delete(strippedKey);
1186
+ if (result.path !== void 0) return {
1187
+ ...result,
1188
+ path: filePath
1189
+ };
1190
+ return result;
1191
+ }
1192
+ /**
1162
1193
  * Execute a command via the default backend.
1163
1194
  * Execution is not path-specific, so it always delegates to the default backend.
1164
1195
  *
@@ -1300,14 +1331,10 @@ const FILESYSTEM_TOOL_NAMES = [
1300
1331
  "grep",
1301
1332
  "execute"
1302
1333
  ];
1303
- const TOOLS_EXCLUDED_FROM_EVICTION = [
1304
- "ls",
1305
- "glob",
1306
- "grep",
1307
- "read_file",
1308
- "edit_file",
1309
- "write_file"
1310
- ];
1334
+ function isFilesystemToolName(name) {
1335
+ return typeof name === "string" && FILESYSTEM_TOOL_NAMES.includes(name);
1336
+ }
1337
+ const TOOLS_EXCLUDED_FROM_EVICTION = FILESYSTEM_TOOL_NAMES.filter((name) => name !== "execute");
1311
1338
  /**
1312
1339
  * Maximum size for binary (non-text) files read via read_file, in bytes.
1313
1340
  * Base64-encoded content is ~33% larger, so 10MB raw ≈ 13.3MB in context.
@@ -1475,19 +1502,46 @@ const FilesystemStateSchema = new StateSchema({ files: new ReducedValue(z.record
1475
1502
  inputSchema: z.record(z.string(), FileDataSchema.nullable()).optional(),
1476
1503
  reducer: fileDataReducer
1477
1504
  }) });
1505
+ /** Extract a message string from an unknown thrown value without `instanceof`. */
1506
+ function getErrorMessage$1(error) {
1507
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") return error.message;
1508
+ return String(error);
1509
+ }
1478
1510
  /**
1479
- * Throw a permission-denied error if `path` is denied under `rules`.
1511
+ * Check whether `path` is permitted under `rules` for `operation`, returning an
1512
+ * error string to surface to the model (or `undefined` when allowed).
1480
1513
  *
1481
- * No-op when `rules` is empty (permissive default). Paths that fail
1482
- * `validatePath` are silently skipped the tool's own input validation
1483
- * will surface a better error.
1514
+ * Never throws: an invalid path (non-absolute, or containing `..` or `~`) or a
1515
+ * denied path is a recoverable tool error, not a fatal run-ending one. Such
1516
+ * paths are rejected, never normalized, so they cannot bypass a deny rule or
1517
+ * reach the backend.
1484
1518
  *
1485
1519
  * @internal
1486
1520
  */
1487
- function enforcePermission(rules, operation, path) {
1521
+ function checkPermission(rules, operation, path) {
1488
1522
  if (rules.length === 0) return;
1489
- const canonical = validatePath(path);
1490
- if (decidePathAccess(rules, operation, canonical) === "deny") throw new Error(`Error: permission denied for ${operation} on ${canonical}`);
1523
+ let canonical;
1524
+ try {
1525
+ canonical = validatePath(path);
1526
+ } catch (error) {
1527
+ return `Error: ${getErrorMessage$1(error)}`;
1528
+ }
1529
+ if (decidePathAccess(rules, operation, canonical) === "deny") return `Error: permission denied for ${operation} on ${canonical}`;
1530
+ }
1531
+ /**
1532
+ * Build an error {@link ToolMessage} for a rejected or denied path. Returning a
1533
+ * bare string would be wrapped as a `status: "success"` message whose content
1534
+ * merely starts with "Error:"; marking `status: "error"` reports the failure
1535
+ * accurately so callers and the model can distinguish a real failure from a
1536
+ * successful result.
1537
+ */
1538
+ function toolError(runtime, toolName, message) {
1539
+ return new ToolMessage({
1540
+ content: message,
1541
+ name: toolName,
1542
+ tool_call_id: runtime.toolCall?.id,
1543
+ status: "error"
1544
+ });
1491
1545
  }
1492
1546
  /**
1493
1547
  * Filter a list of filesystem entries to those the rules permit.
@@ -1508,24 +1562,33 @@ function filterByPermissions(entries, rules, operation, getPath) {
1508
1562
  }
1509
1563
  });
1510
1564
  }
1511
- const FILESYSTEM_SYSTEM_PROMPT = context`
1512
- ## Following Conventions
1565
+ const FILESYSTEM_TOOL_DESCRIPTION_LINES = {
1566
+ ls: "ls: list files in a directory (requires absolute path)",
1567
+ read_file: "read_file: read a file from the filesystem",
1568
+ write_file: "write_file: write to a file in the filesystem",
1569
+ edit_file: "edit_file: edit a file in the filesystem",
1570
+ glob: "glob: find files matching a pattern (e.g., \"**/*.py\")",
1571
+ grep: "grep: search for text within files"
1572
+ };
1573
+ function hasFilesystemToolDescription(name) {
1574
+ return name in FILESYSTEM_TOOL_DESCRIPTION_LINES;
1575
+ }
1576
+ function buildFilesystemSystemPrompt(visibleTools) {
1577
+ const promptToolNames = FILESYSTEM_TOOL_NAMES.filter((name) => visibleTools.has(name));
1578
+ return context`
1579
+ ## Following Conventions
1513
1580
 
1514
- - Read files before editing — understand existing content before making changes
1515
- - Mimic existing style, naming conventions, and patterns
1581
+ - Read files before editing — understand existing content before making changes
1582
+ - Mimic existing style, naming conventions, and patterns
1516
1583
 
1517
- ## Filesystem Tools \`ls\`, \`read_file\`, \`write_file\`, \`edit_file\`, \`glob\`, \`grep\`
1584
+ ## Filesystem Tools ${promptToolNames.map((name) => `\`${name}\``).join(", ")}
1518
1585
 
1519
- You have access to a filesystem which you can interact with using these tools.
1520
- All file paths must start with a /.
1586
+ You have access to a filesystem which you can interact with using these tools.
1587
+ All file paths must start with a /.
1521
1588
 
1522
- - ls: list files in a directory (requires absolute path)
1523
- - read_file: read a file from the filesystem
1524
- - write_file: write to a file in the filesystem
1525
- - edit_file: edit a file in the filesystem
1526
- - glob: find files matching a pattern (e.g., "**/*.py")
1527
- - grep: search for text within files
1528
- `;
1589
+ ${promptToolNames.filter(hasFilesystemToolDescription).map((name) => `- ${FILESYSTEM_TOOL_DESCRIPTION_LINES[name]}`).join("\n")}
1590
+ `;
1591
+ }
1529
1592
  const LS_TOOL_DESCRIPTION = context`
1530
1593
  Lists all files in a directory.
1531
1594
 
@@ -1649,7 +1712,8 @@ const EXECUTION_SYSTEM_PROMPT = context`
1649
1712
  function createLsTool(backend, options) {
1650
1713
  const { customDescription, permissions } = options;
1651
1714
  return tool(async (input, runtime) => {
1652
- enforcePermission(permissions, "read", input.path ?? "/");
1715
+ const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1716
+ if (permissionError !== void 0) return toolError(runtime, "ls", permissionError);
1653
1717
  const resolvedBackend = await resolveBackend(backend, runtime);
1654
1718
  const path = input.path || "/";
1655
1719
  const lsResult = await resolvedBackend.ls(path);
@@ -1677,7 +1741,8 @@ function createLsTool(backend, options) {
1677
1741
  function createReadFileTool(backend, options) {
1678
1742
  const { customDescription, toolTokenLimitBeforeEvict, permissions } = options;
1679
1743
  return tool(async (input, runtime) => {
1680
- enforcePermission(permissions, "read", input.file_path);
1744
+ const permissionError = checkPermission(permissions, "read", input.file_path);
1745
+ if (permissionError !== void 0) return toolError(runtime, "read_file", permissionError);
1681
1746
  const resolvedBackend = await resolveBackend(backend, runtime);
1682
1747
  const { file_path, offset = 0, limit = 100 } = input;
1683
1748
  const readResult = await resolvedBackend.read(file_path, offset, limit);
@@ -1754,7 +1819,8 @@ function createReadFileTool(backend, options) {
1754
1819
  function createWriteFileTool(backend, options) {
1755
1820
  const { customDescription, permissions } = options;
1756
1821
  return tool(async (input, runtime) => {
1757
- enforcePermission(permissions, "write", input.file_path);
1822
+ const permissionError = checkPermission(permissions, "write", input.file_path);
1823
+ if (permissionError !== void 0) return toolError(runtime, "write_file", permissionError);
1758
1824
  const resolvedBackend = await resolveBackend(backend, runtime);
1759
1825
  const { file_path, content } = input;
1760
1826
  const result = await resolvedBackend.write(file_path, content);
@@ -1785,7 +1851,8 @@ function createWriteFileTool(backend, options) {
1785
1851
  function createEditFileTool(backend, options) {
1786
1852
  const { customDescription, permissions } = options;
1787
1853
  return tool(async (input, runtime) => {
1788
- enforcePermission(permissions, "write", input.file_path);
1854
+ const permissionError = checkPermission(permissions, "write", input.file_path);
1855
+ if (permissionError !== void 0) return toolError(runtime, "edit_file", permissionError);
1789
1856
  const resolvedBackend = await resolveBackend(backend, runtime);
1790
1857
  const { file_path, old_string, new_string, replace_all = false } = input;
1791
1858
  const result = await resolvedBackend.edit(file_path, old_string, new_string, replace_all);
@@ -1818,7 +1885,8 @@ function createEditFileTool(backend, options) {
1818
1885
  function createGlobTool(backend, options) {
1819
1886
  const { customDescription, permissions } = options;
1820
1887
  return tool(async (input, runtime) => {
1821
- enforcePermission(permissions, "read", input.path ?? "/");
1888
+ const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1889
+ if (permissionError !== void 0) return toolError(runtime, "glob", permissionError);
1822
1890
  const resolvedBackend = await resolveBackend(backend, runtime);
1823
1891
  const { pattern, path = "/" } = input;
1824
1892
  const globResult = await resolvedBackend.glob(pattern, path);
@@ -1843,7 +1911,8 @@ function createGlobTool(backend, options) {
1843
1911
  function createGrepTool(backend, options) {
1844
1912
  const { customDescription, permissions } = options;
1845
1913
  return tool(async (input, runtime) => {
1846
- enforcePermission(permissions, "read", input.path ?? "/");
1914
+ const permissionError = checkPermission(permissions, "read", input.path ?? "/");
1915
+ if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
1847
1916
  const resolvedBackend = await resolveBackend(backend, runtime);
1848
1917
  const { pattern, path = "/", glob = null } = input;
1849
1918
  const result = await resolvedBackend.grep(pattern, path, glob);
@@ -1899,6 +1968,12 @@ function createExecuteTool(backend, options) {
1899
1968
  * Returns true only when backend exposes route prefixes (CompositeBackend) and
1900
1969
  * every permission path is scoped under one of them.
1901
1970
  */
1971
+ function normalizeFilesystemTools(tools) {
1972
+ if (tools == null || tools === "all") return null;
1973
+ const enabledTools = new Set(tools);
1974
+ if (!enabledTools.has("read_file")) throw new Error("read_file must be included in tools; it is required by FilesystemMiddleware");
1975
+ return enabledTools;
1976
+ }
1902
1977
  function allPathsScopedToRoutes(permissions, backend) {
1903
1978
  if (!CompositeBackend.isInstance(backend)) return false;
1904
1979
  const prefixes = backend.routePrefixes;
@@ -1906,13 +1981,43 @@ function allPathsScopedToRoutes(permissions, backend) {
1906
1981
  return permissions.every((rule) => rule.paths.every((path) => prefixes.some((prefix) => path.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`))));
1907
1982
  }
1908
1983
  /**
1909
- * Create filesystem middleware with all tools and features.
1984
+ * Create middleware that provides built-in filesystem tools and filesystem-aware
1985
+ * prompt guidance.
1986
+ *
1987
+ * By default, the middleware registers every built-in filesystem tool listed in
1988
+ * {@link FILESYSTEM_TOOL_NAMES}. Use {@link FilesystemMiddlewareOptions.tools}
1989
+ * to narrow that set for read-only, search-only, or otherwise restricted
1990
+ * agents. The allowlist only controls built-in filesystem tools; custom tools
1991
+ * from the agent or other middleware are left untouched.
1992
+ *
1993
+ * The middleware also filters tools whose backend capabilities are unavailable
1994
+ * at request time. In particular, `execute` is only visible when the resolved
1995
+ * backend supports command execution. The filesystem prompt is generated from
1996
+ * the final visible filesystem tools so the model is not instructed to call
1997
+ * tools it cannot see.
1998
+ *
1999
+ * @param options Filesystem middleware configuration.
2000
+ * @returns Agent middleware that contributes filesystem state, tools, prompt
2001
+ * guidance, permission checks, and large-result eviction.
2002
+ *
2003
+ * @example Read-only filesystem middleware
2004
+ * ```ts
2005
+ * const middleware = createFilesystemMiddleware({
2006
+ * tools: ["read_file", "ls", "glob", "grep"],
2007
+ * });
2008
+ * ```
1910
2009
  */
1911
2010
  function createFilesystemMiddleware(options = {}) {
1912
- const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [] } = options;
2011
+ const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4, permissions = [], tools: filesystemTools = null } = options;
2012
+ const enabledFilesystemTools = normalizeFilesystemTools(filesystemTools);
2013
+ const executeToolEnabled = enabledFilesystemTools == null || enabledFilesystemTools.has("execute");
1913
2014
  if (permissions.length > 0) validatePermissionPaths(permissions);
1914
- if (permissions.length > 0 && typeof backend !== "function" && isSandboxBackend(backend) && !allPathsScopedToRoutes(permissions, backend)) throw new Error("Filesystem permissions cannot be used with a backend that supports command execution. Shell commands can access any path, making path-based rules ineffective. Either remove permissions, use a backend without execution support, or use a CompositeBackend with all permission paths scoped to a route prefix.");
1915
- const baseSystemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
2015
+ if (permissions.length > 0 && executeToolEnabled && typeof backend !== "function" && isSandboxBackend(backend) && !allPathsScopedToRoutes(permissions, backend)) throw new Error("Filesystem permissions cannot be used with a backend that supports command execution. Shell commands can access any path, making path-based rules ineffective. Either remove permissions, use a backend without execution support, or use a CompositeBackend with all permission paths scoped to a route prefix.");
2016
+ const baseSystemPrompt = customSystemPrompt ?? null;
2017
+ /**
2018
+ * All tools including execute
2019
+ * (execute will be filtered at runtime if backend doesn't support it)
2020
+ */
1916
2021
  const allToolsByName = {
1917
2022
  ls: createLsTool(backend, {
1918
2023
  customDescription: customToolDescriptions?.ls,
@@ -1944,7 +2049,7 @@ function createFilesystemMiddleware(options = {}) {
1944
2049
  permissions
1945
2050
  })
1946
2051
  };
1947
- const allTools = Object.values(allToolsByName);
2052
+ const allTools = FILESYSTEM_TOOL_NAMES.filter((name) => enabledFilesystemTools == null || enabledFilesystemTools.has(name)).map((name) => allToolsByName[name]);
1948
2053
  async function processToolMessage(msg, runtime, state, fallbackToolCallId) {
1949
2054
  if (!toolTokenLimitBeforeEvict) return {
1950
2055
  message: msg,
@@ -2018,8 +2123,14 @@ function createFilesystemMiddleware(options = {}) {
2018
2123
  }));
2019
2124
  let tools = request.tools;
2020
2125
  if (!supportsExecution) tools = tools.filter((t) => t.name !== "execute");
2021
- let filesystemPrompt = baseSystemPrompt;
2022
- if (supportsExecution) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
2126
+ const visibleFilesystemTools = /* @__PURE__ */ new Set();
2127
+ for (const currentTool of tools) {
2128
+ const toolName = typeof currentTool.name === "string" ? currentTool.name : void 0;
2129
+ if (isFilesystemToolName(toolName)) visibleFilesystemTools.add(toolName);
2130
+ }
2131
+ const executionActive = supportsExecution && visibleFilesystemTools.has("execute");
2132
+ let filesystemPrompt = baseSystemPrompt ?? buildFilesystemSystemPrompt(visibleFilesystemTools);
2133
+ if (executionActive) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
2023
2134
  const newSystemMessage = request.systemMessage.concat(filesystemPrompt);
2024
2135
  let messages = request.messages;
2025
2136
  if (humanMessageTokenLimitBeforeEvict && messages) {
@@ -3457,6 +3568,43 @@ function createSkillsMiddleware(options) {
3457
3568
  });
3458
3569
  }
3459
3570
  //#endregion
3571
+ //#region src/middleware/utils.ts
3572
+ /**
3573
+ * Merge custom middleware into an assembled stack by `.name`.
3574
+ *
3575
+ * Matching custom middleware replaces the existing entry in place. New
3576
+ * middleware is appended after the base stack in caller-provided order.
3577
+ */
3578
+ function mergeMiddleware$1(base, custom) {
3579
+ const merged = new Map(base.map((middleware) => [middleware.name, middleware]));
3580
+ for (const middleware of custom) merged.set(middleware.name, middleware);
3581
+ return [...merged.values()];
3582
+ }
3583
+ function middlewareNames(middleware) {
3584
+ return new Set(middleware.map((entry) => entry.name));
3585
+ }
3586
+ function matchingMiddleware(middleware, names) {
3587
+ return middleware.filter((entry) => names.has(entry.name));
3588
+ }
3589
+ /**
3590
+ * Merge custom middleware into default and tail middleware segments.
3591
+ *
3592
+ * Same-name custom entries replace matching defaults in either segment. Novel
3593
+ * custom entries are inserted between the default and tail segments unless
3594
+ * `appendNew` is false.
3595
+ */
3596
+ function mergeMiddlewareStack(defaultMiddleware, customMiddleware, tailMiddleware = [], options = {}) {
3597
+ const defaultMiddlewareNames = middlewareNames(defaultMiddleware);
3598
+ const tailMiddlewareNames = middlewareNames(tailMiddleware);
3599
+ const knownMiddlewareNames = /* @__PURE__ */ new Set([...defaultMiddlewareNames, ...tailMiddlewareNames]);
3600
+ const novelMiddleware = options.appendNew === false ? [] : customMiddleware.filter((entry) => !knownMiddlewareNames.has(entry.name));
3601
+ return [
3602
+ ...mergeMiddleware$1(defaultMiddleware, matchingMiddleware(customMiddleware, defaultMiddlewareNames)),
3603
+ ...novelMiddleware,
3604
+ ...mergeMiddleware$1(tailMiddleware, matchingMiddleware(customMiddleware, tailMiddlewareNames))
3605
+ ];
3606
+ }
3607
+ //#endregion
3460
3608
  //#region src/middleware/completion_callback.ts
3461
3609
  /**
3462
3610
  * Callback middleware for async subagents.
@@ -5048,6 +5196,28 @@ function createCacheBreakpointMiddleware() {
5048
5196
  });
5049
5197
  }
5050
5198
  //#endregion
5199
+ //#region src/middleware/tool_exclusion.ts
5200
+ function hasToolName(tool) {
5201
+ return tool !== null && typeof tool === "object" && "name" in tool && typeof tool.name === "string";
5202
+ }
5203
+ /**
5204
+ * Create middleware that removes excluded tools after all tool-injecting
5205
+ * middleware has had a chance to add tools to the request.
5206
+ *
5207
+ * @internal
5208
+ */
5209
+ function createToolExclusionMiddleware(excludedTools) {
5210
+ return createMiddleware({
5211
+ name: "_ToolExclusionMiddleware",
5212
+ wrapModelCall(request, handler) {
5213
+ return handler({
5214
+ ...request,
5215
+ tools: request.tools?.filter((tool) => !hasToolName(tool) || !excludedTools.has(tool.name))
5216
+ });
5217
+ }
5218
+ });
5219
+ }
5220
+ //#endregion
5051
5221
  //#region src/profiles/keys.ts
5052
5222
  /**
5053
5223
  * Normalize and validate a profile registry key.
@@ -5751,6 +5921,31 @@ const BASE_AGENT_PROMPT = context`
5751
5921
 
5752
5922
  For longer tasks, provide brief progress updates at reasonable intervals — a concise sentence recapping what you've done and what's next.
5753
5923
  `;
5924
+ const PROMPT_SEPARATOR = "\n\n";
5925
+ /** Normalize legacy system prompt values to the structured representation. */
5926
+ function normalizeSystemPrompt(systemPrompt) {
5927
+ if (systemPrompt === void 0) return {};
5928
+ if (typeof systemPrompt === "string" || SystemMessage.isInstance(systemPrompt)) return { prefix: systemPrompt };
5929
+ return systemPrompt;
5930
+ }
5931
+ /** Assemble prompt parts while preserving structured message content blocks. */
5932
+ function assemblePromptParts(parts) {
5933
+ if (parts.length === 0) return "";
5934
+ if (parts.every((part) => typeof part === "string")) return parts.join(PROMPT_SEPARATOR);
5935
+ const contentBlocks = [];
5936
+ for (const [index, part] of parts.entries()) {
5937
+ if (index > 0) contentBlocks.push({
5938
+ type: "text",
5939
+ text: PROMPT_SEPARATOR
5940
+ });
5941
+ if (SystemMessage.isInstance(part)) contentBlocks.push(...part.contentBlocks);
5942
+ else contentBlocks.push({
5943
+ type: "text",
5944
+ text: part
5945
+ });
5946
+ }
5947
+ return new SystemMessage({ contentBlocks });
5948
+ }
5754
5949
  const BUILTIN_TOOL_NAMES = /* @__PURE__ */ new Set([
5755
5950
  ...FILESYSTEM_TOOL_NAMES,
5756
5951
  ...ASYNC_TASK_TOOL_NAMES,
@@ -5796,6 +5991,8 @@ function createDeepAgent(params = {}) {
5796
5991
  providerHint: getModelProvider(model),
5797
5992
  identifierHint: getModelIdentifier(model)
5798
5993
  });
5994
+ const filesystemTools = FILESYSTEM_TOOL_NAMES.filter((toolName) => !harnessProfile.excludedTools.has(toolName));
5995
+ const profileFilesystemTools = filesystemTools.length === FILESYSTEM_TOOL_NAMES.length || !filesystemTools.includes("read_file") ? void 0 : filesystemTools;
5799
5996
  const toolOverrides = harnessProfile.toolDescriptionOverrides;
5800
5997
  const effectiveTools = Object.keys(toolOverrides).length > 0 ? tools.map((t) => t.name in toolOverrides ? Object.assign(Object.create(Object.getPrototypeOf(t)), t, { description: toolOverrides[t.name] }) : t) : tools;
5801
5998
  const anthropicModel = isAnthropicModel(model);
@@ -5817,23 +6014,26 @@ function createDeepAgent(params = {}) {
5817
6014
  * Only the general-purpose subagent inherits the main agent's skills.
5818
6015
  * If a custom subagent needs skills, it must specify its own `skills` array.
5819
6016
  */
5820
- const normalizeSubagentSpec = (input) => {
6017
+ const createSubagentDefaultMiddleware = (input) => {
5821
6018
  const effectivePermissions = input.permissions ?? permissions;
5822
- const subagentMiddleware = [
6019
+ return [
5823
6020
  todoListMiddleware(),
5824
6021
  createFilesystemMiddleware({
5825
6022
  backend,
5826
- permissions: effectivePermissions
6023
+ permissions: effectivePermissions,
6024
+ tools: profileFilesystemTools
5827
6025
  }),
5828
6026
  createSummarizationMiddleware({ backend }),
5829
6027
  createPatchToolCallsMiddleware(),
5830
6028
  ...input.skills != null && input.skills.length > 0 ? [createSkillsMiddleware({
5831
6029
  backend,
5832
6030
  sources: input.skills
5833
- })] : [],
5834
- ...input.middleware ?? [],
5835
- ...cacheMiddleware
6031
+ })] : []
5836
6032
  ];
6033
+ };
6034
+ const normalizeSubagentSpec = (input) => {
6035
+ let subagentMiddleware = mergeMiddlewareStack(createSubagentDefaultMiddleware(input), input.middleware ?? [], cacheMiddleware);
6036
+ if (harnessProfile.excludedMiddleware.size > 0) subagentMiddleware = subagentMiddleware.filter((middleware) => !harnessProfile.excludedMiddleware.has(middleware.name));
5837
6037
  return {
5838
6038
  ...input,
5839
6039
  tools: input.tools ?? [],
@@ -5854,6 +6054,7 @@ function createDeepAgent(params = {}) {
5854
6054
  skills,
5855
6055
  tools: effectiveTools
5856
6056
  });
6057
+ generalPurposeSpec.middleware = mergeMiddlewareStack(generalPurposeSpec.middleware ?? [], customMiddleware, [], { appendNew: false });
5857
6058
  inlineSubagents.unshift(generalPurposeSpec);
5858
6059
  }
5859
6060
  const skillsMiddleware = skills != null && skills.length > 0 ? [createSkillsMiddleware({
@@ -5864,7 +6065,8 @@ function createDeepAgent(params = {}) {
5864
6065
  todoListMiddleware(),
5865
6066
  createFilesystemMiddleware({
5866
6067
  backend,
5867
- permissions
6068
+ permissions,
6069
+ tools: profileFilesystemTools
5868
6070
  }),
5869
6071
  createSubAgentMiddleware({
5870
6072
  defaultModel: model,
@@ -5876,15 +6078,16 @@ function createDeepAgent(params = {}) {
5876
6078
  createSummarizationMiddleware({ backend }),
5877
6079
  createPatchToolCallsMiddleware()
5878
6080
  ];
5879
- const middleware = [
6081
+ let middleware = mergeMiddlewareStack([
5880
6082
  todoMiddleware,
5881
6083
  ...skillsMiddleware,
5882
6084
  fsMiddleware,
5883
6085
  subagentMiddleware,
5884
6086
  summarizationMiddleware,
5885
6087
  patchToolCallsMiddleware,
5886
- ...asyncSubAgents.length > 0 ? [createAsyncSubAgentMiddleware({ asyncSubAgents })] : [],
5887
- ...customMiddleware,
6088
+ ...asyncSubAgents.length > 0 ? [createAsyncSubAgentMiddleware({ asyncSubAgents })] : []
6089
+ ], customMiddleware, [
6090
+ ...resolveMiddleware(harnessProfile.extraMiddleware),
5888
6091
  ...cacheMiddleware,
5889
6092
  ...memory && memory.length > 0 ? [createMemoryMiddleware({
5890
6093
  backend,
@@ -5892,32 +6095,19 @@ function createDeepAgent(params = {}) {
5892
6095
  addCacheControl: anthropicModel
5893
6096
  })] : [],
5894
6097
  ...interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []
5895
- ];
5896
- const profileMiddleware = resolveMiddleware(harnessProfile.extraMiddleware);
5897
- if (profileMiddleware.length > 0) {
5898
- const cacheIdx = middleware.findIndex((m) => m.name === "AnthropicPromptCachingMiddleware");
5899
- if (cacheIdx !== -1) middleware.splice(cacheIdx, 0, ...profileMiddleware);
5900
- else middleware.push(...profileMiddleware);
5901
- }
6098
+ ]);
5902
6099
  if (harnessProfile.excludedMiddleware.size > 0) {
5903
6100
  const excluded = harnessProfile.excludedMiddleware;
5904
- const filtered = middleware.filter((m) => !excluded.has(m.name));
5905
- middleware.length = 0;
5906
- middleware.push(...filtered);
5907
- }
5908
- if (harnessProfile.excludedTools.size > 0) {
5909
- const excludedTools = harnessProfile.excludedTools;
5910
- middleware.push(createMiddleware({
5911
- name: "_ToolExclusionMiddleware",
5912
- wrapModelCall: async (request, handler) => {
5913
- return handler({
5914
- ...request,
5915
- tools: request.tools?.filter((t) => !excludedTools.has(t.name))
5916
- });
5917
- }
5918
- }));
5919
- }
5920
- const effectiveBasePrompt = applyProfilePrompt(harnessProfile, BASE_AGENT_PROMPT);
6101
+ middleware = middleware.filter((entry) => !excluded.has(entry.name));
6102
+ }
6103
+ if (harnessProfile.excludedTools.size > 0) middleware.push(createToolExclusionMiddleware(harnessProfile.excludedTools));
6104
+ const promptConfig = normalizeSystemPrompt(systemPrompt);
6105
+ const promptParts = [];
6106
+ if (promptConfig.prefix !== void 0 && promptConfig.prefix !== null) promptParts.push(promptConfig.prefix);
6107
+ const activeBasePrompt = promptConfig.base !== void 0 ? promptConfig.base : harnessProfile.baseSystemPrompt ?? BASE_AGENT_PROMPT;
6108
+ if (activeBasePrompt !== null) promptParts.push(activeBasePrompt);
6109
+ if (promptConfig.suffix) promptParts.push(promptConfig.suffix);
6110
+ if (harnessProfile.systemPromptSuffix) promptParts.push(harnessProfile.systemPromptSuffix);
5921
6111
  /**
5922
6112
  * Return as DeepAgent with proper DeepAgentTypeConfig
5923
6113
  * - Response: InferStructuredResponse<TResponse> (unwraps ToolStrategy<T>/ProviderStrategy<T> → T)
@@ -5930,19 +6120,7 @@ function createDeepAgent(params = {}) {
5930
6120
  */
5931
6121
  return createAgent({
5932
6122
  model,
5933
- systemPrompt: typeof systemPrompt === "string" ? new SystemMessage({ contentBlocks: [{
5934
- type: "text",
5935
- text: systemPrompt
5936
- }, {
5937
- type: "text",
5938
- text: effectiveBasePrompt
5939
- }] }) : SystemMessage.isInstance(systemPrompt) ? new SystemMessage({ contentBlocks: [...systemPrompt.contentBlocks, {
5940
- type: "text",
5941
- text: effectiveBasePrompt
5942
- }] }) : new SystemMessage({ contentBlocks: [{
5943
- type: "text",
5944
- text: effectiveBasePrompt
5945
- }] }),
6123
+ systemPrompt: assemblePromptParts(promptParts),
5946
6124
  stateSchema,
5947
6125
  tools: effectiveTools,
5948
6126
  middleware,
@@ -6294,6 +6472,19 @@ var StoreBackend = class {
6294
6472
  }
6295
6473
  }
6296
6474
  /**
6475
+ * Delete a file from the store.
6476
+ *
6477
+ * The file path is used as an exact store key. Wildcards are treated
6478
+ * literally and do not expand to multiple entries.
6479
+ */
6480
+ async delete(filePath) {
6481
+ const store = this.getStore();
6482
+ const namespace = this.getNamespace();
6483
+ if (!await store.get(namespace, filePath)) return { error: `Error: File '${filePath}' not found` };
6484
+ await store.delete(namespace, filePath);
6485
+ return { path: filePath };
6486
+ }
6487
+ /**
6297
6488
  * Search file contents for a literal text pattern.
6298
6489
  * Binary files are skipped.
6299
6490
  */
@@ -6506,10 +6697,10 @@ var ContextHubBackend = class ContextHubBackend {
6506
6697
  if (this.cache === null) throw new Error("Context Hub cache failed to initialize");
6507
6698
  return this.cache;
6508
6699
  }
6509
- async commit(files) {
6510
- if (Object.keys(files).length === 0) return;
6700
+ async commit(changes) {
6701
+ if (Object.keys(changes).length === 0) return;
6511
6702
  const payload = {};
6512
- for (const [path, content] of Object.entries(files)) payload[path] = {
6703
+ for (const [path, content] of Object.entries(changes)) payload[path] = content === null ? null : {
6513
6704
  type: "file",
6514
6705
  content
6515
6706
  };
@@ -6519,7 +6710,14 @@ var ContextHubBackend = class ContextHubBackend {
6519
6710
  });
6520
6711
  const match = URL_COMMIT_SUFFIX_RE.exec(url);
6521
6712
  if (match) this.commitHash = match[1];
6522
- if (this.cache !== null) for (const [path, content] of Object.entries(files)) this.cache[path] = content;
6713
+ if (this.cache !== null) {
6714
+ const deletions = new Set(Object.entries(changes).filter(([, content]) => content === null).map(([path]) => path));
6715
+ const updates = Object.fromEntries(Object.entries(changes).filter((entry) => entry[1] !== null));
6716
+ this.cache = {
6717
+ ...Object.fromEntries(Object.entries(this.cache).filter(([path]) => !deletions.has(path))),
6718
+ ...updates
6719
+ };
6720
+ }
6523
6721
  }
6524
6722
  /**
6525
6723
  * Return linked-entry paths mapped to their repo handles.
@@ -6678,6 +6876,20 @@ var ContextHubBackend = class ContextHubBackend {
6678
6876
  throw error;
6679
6877
  }
6680
6878
  }
6879
+ async delete(filePath) {
6880
+ const hubPath = ContextHubBackend.stripPrefix(filePath);
6881
+ try {
6882
+ if (!(hubPath in await this.ensureCache())) return { error: `Error: File '${filePath}' not found` };
6883
+ await this.commit({ [hubPath]: null });
6884
+ return { path: filePath };
6885
+ } catch (error) {
6886
+ if (isLangSmithError(error)) {
6887
+ this.cache = null;
6888
+ return { error: ContextHubBackend.toHubUnavailableError(error) };
6889
+ }
6890
+ throw error;
6891
+ }
6892
+ }
6681
6893
  async uploadFiles(files) {
6682
6894
  const decoder = new TextDecoder("utf-8", { fatal: true });
6683
6895
  const decoded = [];
@@ -7170,6 +7382,16 @@ var BaseSandbox = class {
7170
7382
  occurrences: count
7171
7383
  };
7172
7384
  }
7385
+ /**
7386
+ * Delete a file from the sandbox via a server-side rm.
7387
+ *
7388
+ * Uses rm -f, so deleting a path that does not exist succeeds silently.
7389
+ */
7390
+ async delete(filePath) {
7391
+ const result = await this.execute(`rm -f ${shellQuote(filePath)}`);
7392
+ if (result.exitCode === 0) return { path: filePath };
7393
+ return { error: `Error deleting file '${filePath}': ${result.output.trim() || "unknown error"}` };
7394
+ }
7173
7395
  };
7174
7396
  //#endregion
7175
7397
  //#region src/backends/langsmith.ts
@@ -7373,4 +7595,4 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
7373
7595
  //#endregion
7374
7596
  export { GENERAL_PURPOSE_SUBAGENT as A, isSandboxProtocol as B, MAX_SKILL_NAME_LENGTH as C, createPatchToolCallsMiddleware as D, filesValue as E, createFilesystemMiddleware as F, getMimeType as G, adaptBackendProtocol as H, CompositeBackend as I, isTextMimeType as K, StateBackend as L, TASK_SYSTEM_PROMPT as M, createSubAgent as N, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as O, createSubAgentMiddleware as P, SandboxError as R, MAX_SKILL_FILE_SIZE as S, createMemoryMiddleware as T, adaptSandboxProtocol as U, resolveBackend as V, checkEmptyContent as W, isAsyncSubAgent as _, createDeepAgent as a, createCompletionCallbackMiddleware as b, generalPurposeSubagentConfigSchema as c, serializeProfile as d, EMPTY_HARNESS_PROFILE as f, createAsyncSubAgentMiddleware as g, ConfigurationError as h, StoreBackend as i, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as j, DEFAULT_SUBAGENT_PROMPT as k, harnessProfileConfigSchema as l, REQUIRED_MIDDLEWARE_NAMES as m, BaseSandbox as n, getHarnessProfile as o, createHarnessProfile as p, performStringReplacement as q, ContextHubBackend as r, registerHarnessProfile as s, LangSmithSandbox as t, parseHarnessProfileConfig as u, computeSummarizationDefaults as v, createSkillsMiddleware as w, MAX_SKILL_DESCRIPTION_LENGTH as x, createSummarizationMiddleware as y, isSandboxBackend as z };
7375
7597
 
7376
- //# sourceMappingURL=langsmith-DjCMSywL.js.map
7598
+ //# sourceMappingURL=langsmith-DVh4u6Za.js.map