deepagents 1.14.2 → 1.15.0

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.
@@ -1,6 +1,6 @@
1
1
  import { AIMessage, HumanMessage, SystemMessage, ToolMessage, anthropicPromptCachingMiddleware, bedrockPromptCachingMiddleware, context, countTokensApproximately, createAgent, createMiddleware, humanInTheLoopMiddleware, todoListMiddleware, tool } from "langchain";
2
2
  import { ContextOverflowError } from "@langchain/core/errors";
3
- import { Command, REMOVE_ALL_MESSAGES, ReducedValue, StateSchema, getConfig, getCurrentTaskInput, getStore, isCommand } from "@langchain/langgraph";
3
+ import { Command, Overwrite, REMOVE_ALL_MESSAGES, ReducedValue, StateSchema, getConfig, getCurrentTaskInput, getStore, isCommand } from "@langchain/langgraph";
4
4
  import { z } from "zod/v4";
5
5
  import micromatch from "micromatch";
6
6
  import { createHash } from "node:crypto";
@@ -9,9 +9,30 @@ import * as z$2 from "zod";
9
9
  import { z as z$1 } from "zod";
10
10
  import { initChatModel } from "langchain/chat_models/universal";
11
11
  import yaml from "yaml";
12
+ import { Runnable, RunnableBinding } from "@langchain/core/runnables";
13
+ import { isLangChainTool } from "@langchain/core/tools";
14
+ import { isOpenAITool } from "@langchain/core/language_models/base";
15
+ import { convertToOpenAIFunction } from "@langchain/core/utils/function_calling";
16
+ import { toJsonSchema } from "@langchain/core/utils/json_schema";
17
+ import { isInteropZodSchema } from "@langchain/core/utils/types";
12
18
  import { Client } from "@langchain/langgraph-sdk";
13
19
  import { Client as Client$1 } from "langsmith";
14
20
  import { LangSmithResourceNotFoundError, LangSmithSandboxError, SandboxClient } from "langsmith/experimental/sandbox";
21
+ //#region src/backends/realpath.ts
22
+ const REALPATH = Symbol.for("deepagents.realpath");
23
+ function getRealpath(backend) {
24
+ if (typeof backend !== "object" || backend === null) return;
25
+ const fn = backend[REALPATH];
26
+ return typeof fn === "function" ? fn : void 0;
27
+ }
28
+ function setRealpath(target, fn) {
29
+ Object.defineProperty(target, REALPATH, {
30
+ value: fn,
31
+ enumerable: false,
32
+ configurable: true
33
+ });
34
+ }
35
+ //#endregion
15
36
  //#region src/backends/utils.ts
16
37
  /**
17
38
  * Shared utility functions for memory backend implementations.
@@ -643,6 +664,8 @@ function adaptBackendProtocol(backend) {
643
664
  configurable: true
644
665
  });
645
666
  }
667
+ const realpath = getRealpath(backend);
668
+ if (realpath !== void 0) setRealpath(adapted, realpath);
646
669
  const resolveBackendForPath = backend.resolveBackendForPath;
647
670
  if (typeof resolveBackendForPath === "function") Object.defineProperty(adapted, "resolveBackendForPath", {
648
671
  value: (path) => resolveBackendForPath.call(backend, path),
@@ -1579,11 +1602,13 @@ function validatePermissionPaths(permissions) {
1579
1602
  * - Non-absolute paths (must start with `/`)
1580
1603
  * - Paths containing `..`
1581
1604
  * - Paths containing `~`
1605
+ *
1606
+ * Empty and `.` segments are dropped (`/a/./b` and `/a//b` become `/a/b`).
1582
1607
  */
1583
1608
  function validatePath(raw) {
1584
1609
  if (typeof raw !== "string" || raw.length === 0) throw new Error("path must be a non-empty string");
1585
1610
  if (!raw.startsWith("/")) throw new Error(`path must be absolute: ${JSON.stringify(raw)}`);
1586
- const segments = raw.split("/").filter((s) => s.length > 0);
1611
+ const segments = raw.split("/").filter((s) => s.length > 0 && s !== ".");
1587
1612
  if (segments.includes("..")) throw new Error(`path must not contain "..": ${JSON.stringify(raw)}`);
1588
1613
  if (segments.includes("~")) throw new Error(`path must not contain "~": ${JSON.stringify(raw)}`);
1589
1614
  return `/${segments.join("/")}`;
@@ -1633,14 +1658,15 @@ var CompositeBackend = class {
1633
1658
  sortedRoutes;
1634
1659
  constructor(defaultBackend, routes) {
1635
1660
  this.default = isSandboxProtocol(defaultBackend) ? adaptSandboxProtocol(defaultBackend) : adaptBackendProtocol(defaultBackend);
1636
- this.routes = Object.fromEntries(Object.entries(routes).map(([k, v]) => [k, isSandboxProtocol(v) ? adaptSandboxProtocol(v) : adaptBackendProtocol(v)]));
1661
+ this.routes = Object.fromEntries(Object.entries(routes).map(([k, v]) => [k.endsWith("/") ? k : `${k}/`, isSandboxProtocol(v) ? adaptSandboxProtocol(v) : adaptBackendProtocol(v)]));
1637
1662
  this.sortedRoutes = Object.entries(this.routes).sort((a, b) => b[0].length - a[0].length);
1663
+ setRealpath(this, (path) => this.resolveRealPath(path));
1638
1664
  }
1639
1665
  /** Delegates to default backend's id if it is a sandbox, otherwise empty string. */
1640
1666
  get id() {
1641
1667
  return isSandboxBackend(this.default) ? this.default.id : "";
1642
1668
  }
1643
- /** Route prefixes registered on this backend (e.g. `["/workspace"]`). */
1669
+ /** Route prefixes registered on this backend, normalized to a trailing slash (e.g. `["/workspace/"]`). */
1644
1670
  get routePrefixes() {
1645
1671
  return Object.keys(this.routes);
1646
1672
  }
@@ -1653,11 +1679,29 @@ var CompositeBackend = class {
1653
1679
  static isInstance(backend) {
1654
1680
  return typeof backend === "object" && backend !== null && Array.isArray(backend.routePrefixes);
1655
1681
  }
1656
- /** Returns the backend for `path`, with the matched prefix stripped from the returned path. */
1682
+ /**
1683
+ * Returns the backend for `path`, with the matched prefix stripped (`/foo` → `/`).
1684
+ * Routes match on path-segment boundaries: `/foo` matches `/foo/bar` but not `/foobar`.
1685
+ */
1657
1686
  resolveBackendForPath(path) {
1658
1687
  return this.getBackendAndKey(path);
1659
1688
  }
1660
1689
  /**
1690
+ * Resolve symlinks via the backend that owns `path`, re-adding the route
1691
+ * prefix. Returns `path` unchanged when that backend has no realpath hook.
1692
+ */
1693
+ async resolveRealPath(path) {
1694
+ for (const [routePrefix, backend] of this.sortedRoutes) if (this.isPathWithinRoute(path, routePrefix)) {
1695
+ const realpath = getRealpath(backend);
1696
+ if (realpath === void 0) return path;
1697
+ const suffix = path.substring(routePrefix.length);
1698
+ const resolved = await realpath(suffix ? "/" + suffix : "/");
1699
+ return routePrefix.slice(0, -1) + resolved;
1700
+ }
1701
+ const realpath = getRealpath(this.default);
1702
+ return realpath === void 0 ? path : await realpath(path);
1703
+ }
1704
+ /**
1661
1705
  * Determine which backend handles this key and strip prefix.
1662
1706
  *
1663
1707
  * @param key - Original file path
@@ -2424,25 +2468,42 @@ function getErrorMessage$1(error) {
2424
2468
  return String(error);
2425
2469
  }
2426
2470
  /**
2427
- * Check whether `path` is permitted under `rules` for `operation`, returning an
2428
- * error string to surface to the model (or `undefined` when allowed).
2471
+ * Resolve `canonical` through the backend's realpath hook, when it has one, and
2472
+ * re-validate the result so it is canonical for rule matching. Returns
2473
+ * `canonical` unchanged for backends without path aliasing.
2474
+ */
2475
+ async function resolveRealPath(backend, canonical) {
2476
+ const realpath = getRealpath(backend);
2477
+ return realpath ? validatePath(await realpath(canonical)) : canonical;
2478
+ }
2479
+ /**
2480
+ * Check whether `path` is permitted under `rules` for `operation`.
2429
2481
  *
2430
- * Never throws: an invalid path (non-absolute, or containing `..` or `~`) or a
2431
- * denied path is a recoverable tool error, not a fatal run-ending one. Such
2432
- * paths are rejected, never normalized, so they cannot bypass a deny rule or
2433
- * reach the backend.
2482
+ * Returns the canonical path to hand the backend, or an error string to
2483
+ * surface to the model. The decision covers every form of the target: the
2484
+ * lexical canonical path (dot and empty segments dropped) and, when the
2485
+ * backend has a realpath hook, the symlink-resolved path it would actually
2486
+ * touch. A deny on either blocks the call.
2487
+ *
2488
+ * Never throws: an invalid path (non-absolute, or containing `..` or `~`), one
2489
+ * the backend cannot resolve, or a denied one is a recoverable tool error, not
2490
+ * a fatal run-ending one. With no rules configured, `path` is returned as-is.
2434
2491
  *
2435
2492
  * @internal
2436
2493
  */
2437
- function checkPermission(rules, operation, path) {
2438
- if (rules.length === 0) return;
2494
+ async function checkPermission(backend, rules, operation, path) {
2495
+ if (rules.length === 0) return { path };
2439
2496
  let canonical;
2497
+ let real;
2440
2498
  try {
2441
2499
  canonical = validatePath(path);
2500
+ if (decidePathAccess(rules, operation, canonical) === "deny") return { error: `Error: permission denied for ${operation} on ${canonical}` };
2501
+ real = await resolveRealPath(backend, canonical);
2442
2502
  } catch (error) {
2443
- return `Error: ${getErrorMessage$1(error)}`;
2503
+ return { error: `Error: ${getErrorMessage$1(error)}` };
2444
2504
  }
2445
- if (decidePathAccess(rules, operation, canonical) === "deny") return `Error: permission denied for ${operation} on ${canonical}`;
2505
+ if (real !== canonical && decidePathAccess(rules, operation, real) === "deny") return { error: `Error: permission denied for ${operation} on ${canonical} (resolves to ${real})` };
2506
+ return { path: canonical };
2446
2507
  }
2447
2508
  /**
2448
2509
  * Build an error {@link ToolMessage} for a rejected or denied path. Returning a
@@ -2615,27 +2676,56 @@ function parentPath(path) {
2615
2676
  if (parts.length <= 1) return "/";
2616
2677
  return `/${parts.slice(0, -1).join("/")}`;
2617
2678
  }
2679
+ /**
2680
+ * Resolve the path a delete of `target` would actually remove. Backends unlink
2681
+ * a symlink leaf rather than following it, so only the parent is resolved.
2682
+ */
2683
+ async function resolveDeleteRealPath(backend, target) {
2684
+ const parts = posixParts(target);
2685
+ if (parts.length === 0) return resolveRealPath(backend, target);
2686
+ return validatePath(`${await resolveRealPath(backend, parentPath(target))}/${parts[parts.length - 1]}`);
2687
+ }
2618
2688
  function supportsDelete(backend) {
2619
2689
  return typeof backend.delete === "function";
2620
2690
  }
2621
2691
  /**
2622
2692
  * Filter a list of filesystem entries to those the rules permit.
2623
2693
  *
2624
- * `getPath` extracts the absolute path from each entry. Entries with
2625
- * unparsable paths are included (not silently dropped). Returns the
2626
- * original array unchanged when `rules` is empty.
2694
+ * `getPath` extracts the absolute path from each entry. Each entry is checked
2695
+ * on its canonical path and, when the backend has a realpath hook, on its
2696
+ * symlink-resolved path. Entries with unparsable paths are included; entries
2697
+ * whose path cannot be resolved are dropped.
2698
+ * Returns the original array unchanged when `rules` is empty.
2627
2699
  *
2628
2700
  * @internal
2629
2701
  */
2630
- function filterByPermissions(entries, rules, operation, getPath) {
2702
+ async function filterByPermissions(backend, entries, rules, operation, getPath) {
2631
2703
  if (rules.length === 0) return entries;
2632
- return entries.filter((entry) => {
2704
+ const decisions = /* @__PURE__ */ new Map();
2705
+ const isPermitted = async (rawPath) => {
2706
+ let canonical;
2633
2707
  try {
2634
- return decidePathAccess(rules, operation, validatePath(getPath(entry))) !== "deny";
2708
+ canonical = validatePath(rawPath);
2635
2709
  } catch {
2636
2710
  return true;
2637
2711
  }
2638
- });
2712
+ if (decidePathAccess(rules, operation, canonical) === "deny") return false;
2713
+ try {
2714
+ return decidePathAccess(rules, operation, await resolveRealPath(backend, canonical)) !== "deny";
2715
+ } catch {
2716
+ return false;
2717
+ }
2718
+ };
2719
+ const permitted = await Promise.all(entries.map((entry) => {
2720
+ const rawPath = getPath(entry);
2721
+ let decision = decisions.get(rawPath);
2722
+ if (decision === void 0) {
2723
+ decision = isPermitted(rawPath);
2724
+ decisions.set(rawPath, decision);
2725
+ }
2726
+ return decision;
2727
+ }));
2728
+ return entries.filter((_, index) => permitted[index]);
2639
2729
  }
2640
2730
  const LS_TOOL_DESCRIPTION = context`
2641
2731
  Lists all files in a directory.
@@ -2723,13 +2813,13 @@ function getExecuteToolDescription(hasGrep, hasGlob) {
2723
2813
  function createLsTool(backend, options) {
2724
2814
  const { customDescription, permissions } = options;
2725
2815
  return tool(async (input, runtime) => {
2726
- const permissionError = checkPermission(permissions, "read", input.path ?? "/");
2727
- if (permissionError !== void 0) return toolError(runtime, "ls", permissionError);
2728
2816
  const resolvedBackend = await resolveBackend(backend, runtime);
2729
2817
  const path = input.path || "/";
2730
- const lsResult = await resolvedBackend.ls(path);
2818
+ const permission = await checkPermission(resolvedBackend, permissions, "read", path);
2819
+ if (permission.error !== void 0) return toolError(runtime, "ls", permission.error);
2820
+ const lsResult = await resolvedBackend.ls(permission.path);
2731
2821
  if (lsResult.error) return `Error listing files: ${lsResult.error}`;
2732
- const infos = filterByPermissions(lsResult.files ?? [], permissions, "read", (info) => info.path);
2822
+ const infos = await filterByPermissions(resolvedBackend, lsResult.files ?? [], permissions, "read", (info) => info.path);
2733
2823
  if (infos.length === 0) return `No files found in ${path}`;
2734
2824
  const lines = [];
2735
2825
  for (const info of infos) if (info.is_dir) lines.push(`${info.path} (directory)`);
@@ -2752,23 +2842,17 @@ function createLsTool(backend, options) {
2752
2842
  function createReadFileTool(backend, options) {
2753
2843
  const { customDescription, toolTokenLimitBeforeEvict, permissions } = options;
2754
2844
  return tool(async (input, runtime) => {
2755
- const permissionError = checkPermission(permissions, "read", input.file_path);
2756
- if (permissionError !== void 0) return toolError(runtime, "read_file", permissionError);
2757
2845
  const resolvedBackend = await resolveBackend(backend, runtime);
2846
+ const permission = await checkPermission(resolvedBackend, permissions, "read", input.file_path);
2847
+ if (permission.error !== void 0) return toolError(runtime, "read_file", permission.error);
2758
2848
  const { file_path, offset: requestedOffset = 0, limit: requestedLimit = 100 } = input;
2759
2849
  const { offset, limit } = normalizeReadPagination(requestedOffset, requestedLimit);
2760
- const readResult = await resolvedBackend.read(file_path, offset, limit);
2761
- if (readResult.error) return [{
2762
- type: "text",
2763
- text: `Error: ${readResult.error}`
2764
- }];
2850
+ const readResult = await resolvedBackend.read(permission.path, offset, limit);
2851
+ if (readResult.error) return toolError(runtime, "read_file", `Error: ${readResult.error}`);
2765
2852
  const mimeType = readResult.mimeType ?? getMimeType(file_path);
2766
2853
  if (!isTextMimeType(mimeType)) {
2767
2854
  const binaryContent = readResult.content;
2768
- if (!binaryContent) return [{
2769
- type: "text",
2770
- text: `Error: expected binary content for '${file_path}'`
2771
- }];
2855
+ if (!binaryContent) return toolError(runtime, "read_file", `Error: expected binary content for '${file_path}'`);
2772
2856
  let base64Data;
2773
2857
  if (typeof binaryContent === "string") base64Data = binaryContent;
2774
2858
  else if (ArrayBuffer.isView(binaryContent)) base64Data = Buffer.from(binaryContent).toString("base64");
@@ -2777,10 +2861,7 @@ function createReadFileTool(backend, options) {
2777
2861
  base64Data = Buffer.from(new Uint8Array(values)).toString("base64");
2778
2862
  }
2779
2863
  const sizeBytes = Math.ceil(base64Data.length * 3 / 4);
2780
- if (sizeBytes > 10485760) return [{
2781
- type: "text",
2782
- text: `Error: file too large to read (${Math.round(sizeBytes / 1048576)}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / 1048576}MB limit for binary files)`
2783
- }];
2864
+ if (sizeBytes > 10485760) return toolError(runtime, "read_file", `Error: file too large to read (${Math.round(sizeBytes / 1048576)}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / 1048576}MB limit for binary files)`);
2784
2865
  if (mimeType.startsWith("image/")) return [{
2785
2866
  type: "image",
2786
2867
  mimeType,
@@ -2846,11 +2927,11 @@ function createReadFileTool(backend, options) {
2846
2927
  function createWriteFileTool(backend, options) {
2847
2928
  const { customDescription, permissions } = options;
2848
2929
  return tool(async (input, runtime) => {
2849
- const permissionError = checkPermission(permissions, "write", input.file_path);
2850
- if (permissionError !== void 0) return toolError(runtime, "write_file", permissionError);
2851
2930
  const resolvedBackend = await resolveBackend(backend, runtime);
2931
+ const permission = await checkPermission(resolvedBackend, permissions, "write", input.file_path);
2932
+ if (permission.error !== void 0) return toolError(runtime, "write_file", permission.error);
2852
2933
  const { file_path, content } = input;
2853
- const result = await resolvedBackend.write(file_path, content);
2934
+ const result = await resolvedBackend.write(permission.path, content);
2854
2935
  if (result.error) return result.error;
2855
2936
  const message = new ToolMessage({
2856
2937
  content: `Successfully wrote to '${file_path}'`,
@@ -2878,11 +2959,11 @@ function createWriteFileTool(backend, options) {
2878
2959
  function createEditFileTool(backend, options) {
2879
2960
  const { customDescription, permissions } = options;
2880
2961
  return tool(async (input, runtime) => {
2881
- const permissionError = checkPermission(permissions, "write", input.file_path);
2882
- if (permissionError !== void 0) return toolError(runtime, "edit_file", permissionError);
2883
2962
  const resolvedBackend = await resolveBackend(backend, runtime);
2963
+ const permission = await checkPermission(resolvedBackend, permissions, "write", input.file_path);
2964
+ if (permission.error !== void 0) return toolError(runtime, "edit_file", permission.error);
2884
2965
  const { file_path, old_string, new_string, replace_all = false } = input;
2885
- const result = await resolvedBackend.edit(file_path, old_string, new_string, replace_all);
2966
+ const result = await resolvedBackend.edit(permission.path, old_string, new_string, replace_all);
2886
2967
  if (result.error) return result.error;
2887
2968
  const message = new ToolMessage({
2888
2969
  content: `Successfully replaced ${result.occurrences} occurrence(s) in '${file_path}'`,
@@ -2922,6 +3003,17 @@ function createDeleteTool(backend, options) {
2922
3003
  const hasDescendants = await deleteTargetMayHaveDescendants(resolvedBackend, validatedPath, permissions.length > 0);
2923
3004
  const denyingPatterns = findDeleteDenyPatterns(permissions, validatedPath, hasDescendants);
2924
3005
  if (denyingPatterns.length > 0) return toolError(runtime, "delete", `Error: permission denied for write on ${validatedPath} (matches deny rule(s): ${denyingPatterns.join(", ")})`);
3006
+ if (permissions.length > 0) {
3007
+ let realTarget;
3008
+ try {
3009
+ realTarget = await resolveDeleteRealPath(resolvedBackend, validatedPath);
3010
+ } catch (error) {
3011
+ return toolError(runtime, "delete", `Error: ${getErrorMessage$1(error)}`);
3012
+ }
3013
+ if (realTarget !== validatedPath) {
3014
+ if (findDeleteDenyPatterns(permissions, realTarget, hasDescendants).length > 0) return toolError(runtime, "delete", `Error: permission denied for write on ${validatedPath} (resolves to ${realTarget})`);
3015
+ }
3016
+ }
2925
3017
  if (!supportsDelete(resolvedBackend)) return toolError(runtime, "delete", `Error: deletion is not available for '${validatedPath}'.`);
2926
3018
  const result = await resolvedBackend.delete(validatedPath);
2927
3019
  if (result.error) return toolError(runtime, "delete", result.error);
@@ -2948,13 +3040,13 @@ function createDeleteTool(backend, options) {
2948
3040
  function createGlobTool(backend, options) {
2949
3041
  const { customDescription, permissions } = options;
2950
3042
  return tool(async (input, runtime) => {
2951
- const permissionError = checkPermission(permissions, "read", input.path ?? "/");
2952
- if (permissionError !== void 0) return toolError(runtime, "glob", permissionError);
2953
3043
  const resolvedBackend = await resolveBackend(backend, runtime);
2954
- const { pattern, path } = input;
2955
- const globResult = await resolvedBackend.glob(pattern, path);
3044
+ const { pattern } = input;
3045
+ const permission = await checkPermission(resolvedBackend, permissions, "read", input.path ?? "/");
3046
+ if (permission.error !== void 0) return toolError(runtime, "glob", permission.error);
3047
+ const globResult = await resolvedBackend.glob(pattern, input.path === void 0 ? void 0 : permission.path);
2956
3048
  if (globResult.error) return `Error finding files: ${globResult.error}`;
2957
- const infos = filterByPermissions(globResult.files ?? [], permissions, "read", (info) => info.path);
3049
+ const infos = await filterByPermissions(resolvedBackend, globResult.files ?? [], permissions, "read", (info) => info.path);
2958
3050
  if (infos.length === 0) return `No files found matching pattern '${pattern}'`;
2959
3051
  const result = truncateIfTooLong(infos.map((info) => info.path));
2960
3052
  if (Array.isArray(result)) return result.join("\n");
@@ -2974,14 +3066,14 @@ function createGlobTool(backend, options) {
2974
3066
  function createGrepTool(backend, options) {
2975
3067
  const { customDescription, permissions, includeExecution, grepMaxCount } = options;
2976
3068
  return tool(async (input, runtime) => {
2977
- const permissionError = checkPermission(permissions, "read", input.path ?? "/");
2978
- if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
2979
3069
  const resolvedBackend = await resolveBackend(backend, runtime);
2980
3070
  const { pattern, path = "/", glob = null, output_mode = "content" } = input;
3071
+ const permission = await checkPermission(resolvedBackend, permissions, "read", path);
3072
+ if (permission.error !== void 0) return toolError(runtime, "grep", permission.error);
2981
3073
  const maxCount = input.max_count ?? grepMaxCount;
2982
- const result = await resolvedBackend.grep(pattern, path, glob, maxCount);
3074
+ const result = await resolvedBackend.grep(pattern, permission.path, glob, maxCount);
2983
3075
  if (result.error) return result.error;
2984
- const matches = filterByPermissions(result.matches ?? [], permissions, "read", (m) => m.path);
3076
+ const matches = await filterByPermissions(resolvedBackend, result.matches ?? [], permissions, "read", (m) => m.path);
2985
3077
  if (matches.length === 0) return `No matches found for pattern '${pattern}'`;
2986
3078
  const truncated = truncateIfTooLong(formatGrepMatches(matches, output_mode));
2987
3079
  let content = typeof truncated === "string" ? truncated : truncated.join("\n");
@@ -4173,10 +4265,12 @@ const EXCLUDED_STATE_KEYS = [
4173
4265
  "todos",
4174
4266
  "structuredResponse",
4175
4267
  "skillsMetadata",
4268
+ "pinnedSkills",
4176
4269
  "memoryContents",
4177
4270
  ...CALL_COUNT_STATE_KEYS,
4178
4271
  "_summarizationEvent",
4179
4272
  "_summarizationSessionId",
4273
+ "_skillToolsDisclosed",
4180
4274
  FORKED_CONTEXT_KEY
4181
4275
  ];
4182
4276
  /**
@@ -4195,9 +4289,11 @@ const EXCLUDED_STATE_KEYS = [
4195
4289
  */
4196
4290
  const FORK_EXCLUDED_STATE_KEYS = [
4197
4291
  "structuredResponse",
4292
+ "pinnedSkills",
4198
4293
  ...CALL_COUNT_STATE_KEYS,
4199
4294
  "_summarizationEvent",
4200
- "_summarizationSessionId"
4295
+ "_summarizationSessionId",
4296
+ "_skillToolsDisclosed"
4201
4297
  ];
4202
4298
  /**
4203
4299
  * Default description for the general-purpose subagent.
@@ -4640,7 +4736,8 @@ function patchDanglingToolCalls(messages) {
4640
4736
  patchedMessages.push(new ToolMessage({
4641
4737
  content: toolMsg,
4642
4738
  name: toolCall.name,
4643
- tool_call_id: toolCall.id
4739
+ tool_call_id: toolCall.id,
4740
+ status: "error"
4644
4741
  }));
4645
4742
  }
4646
4743
  }
@@ -5082,6 +5179,46 @@ function createMemoryMiddleware(options) {
5082
5179
  });
5083
5180
  }
5084
5181
  //#endregion
5182
+ //#region src/errors.ts
5183
+ const CONFIGURATION_ERROR_SYMBOL = Symbol.for("deepagents.configuration_error");
5184
+ /**
5185
+ * Thrown when `createDeepAgent` receives invalid configuration.
5186
+ *
5187
+ * Follows the same pattern as {@link SandboxError}: a human-readable
5188
+ * `message`, a structured `code` for programmatic handling, and a
5189
+ * static `isInstance` guard that works across realms.
5190
+ *
5191
+ * @example
5192
+ * ```typescript
5193
+ * try {
5194
+ * createDeepAgent({ tools: [myTool] });
5195
+ * } catch (error) {
5196
+ * if (ConfigurationError.isInstance(error)) {
5197
+ * switch (error.code) {
5198
+ * case "TOOL_NAME_COLLISION":
5199
+ * console.error("Rename your tool:", error.message);
5200
+ * break;
5201
+ * }
5202
+ * }
5203
+ * }
5204
+ * ```
5205
+ */
5206
+ var ConfigurationError = class ConfigurationError extends Error {
5207
+ code;
5208
+ cause;
5209
+ [CONFIGURATION_ERROR_SYMBOL] = true;
5210
+ name = "ConfigurationError";
5211
+ constructor(message, code, cause) {
5212
+ super(message);
5213
+ this.code = code;
5214
+ this.cause = cause;
5215
+ Object.setPrototypeOf(this, ConfigurationError.prototype);
5216
+ }
5217
+ static isInstance(error) {
5218
+ return typeof error === "object" && error !== null && error[CONFIGURATION_ERROR_SYMBOL] === true;
5219
+ }
5220
+ };
5221
+ //#endregion
5085
5222
  //#region src/middleware/skills.ts
5086
5223
  /**
5087
5224
  * Backend-agnostic skills middleware for loading agent skills from any backend.
@@ -5141,6 +5278,10 @@ const SKILL_MODULE_EXTENSIONS = [
5141
5278
  ".jsx",
5142
5279
  ".tsx"
5143
5280
  ];
5281
+ /** `SKILL.md` frontmatter `metadata` key listing the skill's include names, space-separated. */
5282
+ const INCLUDE_TOOLS_KEY = "include_tools";
5283
+ /** YAML frontmatter between `---` delimiters at the start of a `SKILL.md`. */
5284
+ const FRONTMATTER_PATTERN = /^---\s*\n([\s\S]*?)\n---\s*\n/;
5144
5285
  /**
5145
5286
  * Zod schema for a single skill metadata entry.
5146
5287
  */
@@ -5182,11 +5323,52 @@ const SkillMetadataEntrySchema = z$1.object({
5182
5323
  */
5183
5324
  const skillsMetadataValue = z$1.array(SkillMetadataEntrySchema).nullish();
5184
5325
  /**
5326
+ * State value for a middleware's `pinnedSkills` field: the names of skills to
5327
+ * pin.
5328
+ *
5329
+ * The next time the skills middleware's `beforeModel` runs, before a model
5330
+ * call, each named skill's `SKILL.md`, without its frontmatter, is appended to
5331
+ * the conversation as its own `HumanMessage`, in the order named. The message
5332
+ * is a snapshot: editing the file later doesn't change it, and naming the
5333
+ * skill again appends a new copy.
5334
+ *
5335
+ * Names only, matched exactly against the loaded skills. A name that isn't
5336
+ * loaded, or whose `SKILL.md` can't be read, is skipped without an error. The
5337
+ * key is cleared once consumed, so a later run pins nothing unless it names
5338
+ * skills again. Writes in the same step add up, so parallel tool calls can
5339
+ * each pin skills by returning a `Command`.
5340
+ *
5341
+ * Like {@link skillsMetadataValue}, treat the value as opaque. A middleware
5342
+ * declares it on its state schema to set `pinnedSkills` from its own hooks.
5343
+ *
5344
+ * @example
5345
+ * ```typescript
5346
+ * import { createMiddleware } from "langchain";
5347
+ * import { StateSchema } from "@langchain/langgraph";
5348
+ * import { pinnedSkillsValue } from "deepagents";
5349
+ *
5350
+ * const routeSkills = createMiddleware({
5351
+ * name: "RouteSkills",
5352
+ * stateSchema: new StateSchema({ pinnedSkills: pinnedSkillsValue }),
5353
+ * beforeAgent: (state) => ({ pinnedSkills: skillsFor(state.messages) }),
5354
+ * });
5355
+ * ```
5356
+ */
5357
+ const pinnedSkillsValue = new ReducedValue(z$1.array(z$1.string()).default(() => []), { reducer: (stored, written) => [...stored ?? [], ...written ?? []] });
5358
+ /**
5185
5359
  * State schema for skills middleware.
5186
5360
  */
5187
5361
  const SkillsStateSchema = new StateSchema({
5188
5362
  skillsMetadata: skillsMetadataValue,
5189
- files: filesValue
5363
+ pinnedSkills: pinnedSkillsValue,
5364
+ files: filesValue,
5365
+ /**
5366
+ * The skill tools disclosed to the latest model call, each mapped to the
5367
+ * include name that produced it, for the tool-time gate. Written on every
5368
+ * model call of a skills middleware with skill tools. The last write wins,
5369
+ * so a model call retried within one step can write it again.
5370
+ */
5371
+ _skillToolsDisclosed: new ReducedValue(z$1.record(z$1.string(), z$1.string()).optional(), { reducer: (_previous, next) => next })
5190
5372
  });
5191
5373
  /**
5192
5374
  * Skills System Documentation prompt template.
@@ -5288,7 +5470,8 @@ function validateSkillName(name, directoryName) {
5288
5470
  *
5289
5471
  * YAML parsing can return any type for the `metadata` key. This ensures the
5290
5472
  * value in {@link SkillMetadata} is always a `Record<string, string>` by
5291
- * coercing via `String()` and rejecting non-object inputs.
5473
+ * coercing via `String()` and rejecting non-object inputs. It also warns when
5474
+ * `include_tools` isn't a space-separated string of tool names.
5292
5475
  *
5293
5476
  * @param raw - Raw value from `frontmatterData.metadata`.
5294
5477
  * @param skillPath - Path to the `SKILL.md` file (for warning messages).
@@ -5299,6 +5482,8 @@ function validateMetadata(raw, skillPath) {
5299
5482
  if (raw) console.warn(`Ignoring non-object metadata in ${skillPath} (got ${typeof raw})`);
5300
5483
  return {};
5301
5484
  }
5485
+ const includeTools = raw[INCLUDE_TOOLS_KEY];
5486
+ if (Array.isArray(includeTools) || typeof includeTools === "string" && includeTools.includes(",")) console.warn(`metadata.include_tools in ${skillPath} should be a space-separated string of tool names; got ${JSON.stringify(includeTools)}`);
5302
5487
  const result = {};
5303
5488
  for (const [k, v] of Object.entries(raw)) result[String(k)] = String(v);
5304
5489
  return result;
@@ -5336,7 +5521,7 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
5336
5521
  console.warn(`Skipping ${skillPath}: content too large (${content.length} bytes)`);
5337
5522
  return null;
5338
5523
  }
5339
- const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
5524
+ const match = content.match(FRONTMATTER_PATTERN);
5340
5525
  if (!match) {
5341
5526
  console.warn(`Skipping ${skillPath}: no valid YAML frontmatter found`);
5342
5527
  return null;
@@ -5455,6 +5640,61 @@ async function listSkillsFromBackend(backend, sourcePath) {
5455
5640
  return skills;
5456
5641
  }
5457
5642
  /**
5643
+ * Load the skills in every source, in order. A later source's skill replaces
5644
+ * an earlier one of the same name.
5645
+ */
5646
+ async function loadSkills(backend, sources) {
5647
+ const allSkills = /* @__PURE__ */ new Map();
5648
+ for (const sourcePath of sources) try {
5649
+ const skills = await listSkillsFromBackend(backend, sourcePath);
5650
+ for (const skill of skills) allSkills.set(skill.name, skill);
5651
+ } catch (error) {
5652
+ console.debug(`[BackendSkillsMiddleware] Failed to load skills from ${sourcePath}:`, error);
5653
+ }
5654
+ return Array.from(allSkills.values());
5655
+ }
5656
+ /**
5657
+ * Return a message pinning each skill `names` refers to, in first-seen order.
5658
+ *
5659
+ * Skips a name that isn't in `skills`, and a skill whose `SKILL.md` is
5660
+ * missing, unreadable, empty, or larger than {@link MAX_SKILL_FILE_SIZE}.
5661
+ */
5662
+ async function buildPinnedSkillMessages(backend, names, skills) {
5663
+ const adaptedBackend = adaptBackendProtocol(backend);
5664
+ const skillsByName = new Map(skills.map((skill) => [skill.name, skill]));
5665
+ const skillsToPin = [...new Set(names)].flatMap((name) => {
5666
+ const skill = skillsByName.get(name);
5667
+ return skill === void 0 ? [] : [skill];
5668
+ });
5669
+ const contents = await Promise.all(skillsToPin.map((skill) => readFileFromBackend(adaptedBackend, skill.path).catch(() => null)));
5670
+ return skillsToPin.flatMap((skill, i) => {
5671
+ const content = contents[i];
5672
+ if (!content || content.length > 10485760) return [];
5673
+ return [buildPinnedSkillMessage(skill, content)];
5674
+ });
5675
+ }
5676
+ /** Return the message pinning `skill`, carrying its `SKILL.md` `content` without frontmatter. */
5677
+ function buildPinnedSkillMessage(skill, content) {
5678
+ const body = content.replace(FRONTMATTER_PATTERN, "").trimEnd();
5679
+ const name = escapeXmlAttribute(skill.name);
5680
+ const path = escapeXmlAttribute(skill.path);
5681
+ return new HumanMessage$1({
5682
+ content: `<skill name="${name}" path="${path}">\n${body}\n</skill>`,
5683
+ additional_kwargs: {
5684
+ lc_source: "pinned_skill",
5685
+ skill: {
5686
+ name: skill.name,
5687
+ path: skill.path,
5688
+ description: skill.description
5689
+ }
5690
+ }
5691
+ });
5692
+ }
5693
+ /** Escape `value` for a double-quoted XML attribute. */
5694
+ function escapeXmlAttribute(value) {
5695
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
5696
+ }
5697
+ /**
5458
5698
  * Format skills locations for display in system prompt.
5459
5699
  * Shows priority indicator for the last source (highest priority).
5460
5700
  */
@@ -5514,6 +5754,336 @@ function validateModulePath(raw) {
5514
5754
  if (!endsWithModuleExtension(normalized)) return;
5515
5755
  return normalized;
5516
5756
  }
5757
+ /** Tool `extras` key (and provider field) that withholds a tool's schema until searched for. */
5758
+ const DEFER_LOADING = "defer_loading";
5759
+ /**
5760
+ * Model ID prefixes that accept an inline `tool_definition` mid-conversation.
5761
+ *
5762
+ * `@langchain/anthropic` sends any mid-conversation system message in place,
5763
+ * without gating on the model, so this list is the only guard.
5764
+ */
5765
+ const ANTHROPIC_INLINE_TOOL_MODELS = [
5766
+ "claude-opus-5",
5767
+ "claude-fable-5",
5768
+ "claude-mythos-5",
5769
+ "claude-opus-4-8"
5770
+ ];
5771
+ /** Models whose Responses API accepts an `additional_tools` input item, matched with or without a `-` suffix such as a snapshot date. */
5772
+ const OPENAI_INLINE_TOOL_MODELS = ["gpt-6", "gpt-5.6"];
5773
+ /** Root `input_schema` keys the Anthropic API rejects, failing the whole request. */
5774
+ const ANTHROPIC_ROOT_COMBINATORS = [
5775
+ "oneOf",
5776
+ "anyOf",
5777
+ "allOf"
5778
+ ];
5779
+ /** `extras` keys `ChatAnthropic` copies into a tool's definition, bar the ones a disclosure must never carry. */
5780
+ const ANTHROPIC_DEFINITION_EXTRAS = [
5781
+ "input_examples",
5782
+ "allowed_callers",
5783
+ "strict"
5784
+ ];
5785
+ /**
5786
+ * Return the resolver the skills middleware's `tools` option stands for.
5787
+ *
5788
+ * A list is validated, then resolved by exact name.
5789
+ *
5790
+ * @throws {ConfigurationError} If `tools` is neither a list nor a function,
5791
+ * or a list entry isn't a client tool or repeats a name.
5792
+ */
5793
+ function toSkillToolResolver(tools) {
5794
+ if (tools != null && !Array.isArray(tools)) {
5795
+ if (typeof tools !== "function") throw new ConfigurationError(`tools must be an array of tools or a resolver function, got ${typeof tools}; wrap a single tool in an array`, "SKILL_TOOLS_UNSUPPORTED_TYPE");
5796
+ return tools;
5797
+ }
5798
+ const entries = tools ?? [];
5799
+ if (!entries.every(isClientTool)) throw new ConfigurationError("tools entries must be client tools; provider-native tool objects are not supported", "SKILL_TOOLS_UNSUPPORTED_TYPE");
5800
+ const names = entries.map((t) => t.name);
5801
+ const duplicates = [...new Set(names.filter((n, i) => names.indexOf(n) !== i))].sort();
5802
+ if (duplicates.length > 0) throw new ConfigurationError(`tools contains duplicate tool name(s): ${duplicates.join(", ")}`, "SKILL_TOOLS_DUPLICATE_NAME");
5803
+ const byName = new Map(entries.map((t) => [t.name, t]));
5804
+ return (name) => byName.has(name) ? [byName.get(name)] : [];
5805
+ }
5806
+ /** Whether `value` is a runnable client tool rather than a provider-native or plain object. */
5807
+ function isClientTool(value) {
5808
+ return Runnable.isRunnable(value) && typeof value.name === "string";
5809
+ }
5810
+ /**
5811
+ * Call `resolver` for one include name, keeping the first of each tool name.
5812
+ *
5813
+ * @throws {TypeError} If the resolver returns something other than an array
5814
+ * of client tools.
5815
+ */
5816
+ async function callSkillToolResolver(resolver, includeName, runtime) {
5817
+ const result = await resolver(includeName, runtime);
5818
+ if (!Array.isArray(result)) throw new TypeError(`skill tool resolver must return an array of tools for '${includeName}', got ${typeof result}`);
5819
+ const tools = /* @__PURE__ */ new Map();
5820
+ for (const item of result) {
5821
+ if (!isClientTool(item)) throw new TypeError(`skill tool resolver returned a non-tool ${typeof item} for '${includeName}'; expected tool instances`);
5822
+ if (!tools.has(item.name)) tools.set(item.name, item);
5823
+ }
5824
+ return [...tools.values()];
5825
+ }
5826
+ /**
5827
+ * Return every successful `read_file` or pin of a `SKILL.md` that lists
5828
+ * tools, in message order.
5829
+ *
5830
+ * The read's or pin's path must equal the skill's path exactly, as the skills
5831
+ * listing shows it. Any `offset` or `limit` counts, and so does a result whose
5832
+ * content was later truncated or compacted, since only the call and the
5833
+ * result's status are read.
5834
+ */
5835
+ function findSkillReads(messages, skills) {
5836
+ const skillsByPath = /* @__PURE__ */ new Map();
5837
+ for (const skill of skills) {
5838
+ const value = skill.metadata?.[INCLUDE_TOOLS_KEY];
5839
+ const includeNames = typeof value === "string" ? value.split(/\s+/).filter(Boolean) : [];
5840
+ if (includeNames.length > 0) skillsByPath.set(skill.path, { includeNames });
5841
+ }
5842
+ if (skillsByPath.size === 0) return [];
5843
+ const readPaths = /* @__PURE__ */ new Map();
5844
+ for (const message of messages) {
5845
+ if (!AIMessage$1.isInstance(message)) continue;
5846
+ for (const toolCall of message.tool_calls ?? []) if (toolCall.name === "read_file" && toolCall.id) readPaths.set(toolCall.id, toolCall.args?.file_path ?? toolCall.args?.path);
5847
+ }
5848
+ const reads = [];
5849
+ messages.forEach((message, index) => {
5850
+ let path;
5851
+ if (ToolMessage$1.isInstance(message) && message.status !== "error") path = readPaths.get(message.tool_call_id);
5852
+ else if (HumanMessage$1.isInstance(message) && message.additional_kwargs?.lc_source === "pinned_skill") path = message.additional_kwargs.skill?.path;
5853
+ else return;
5854
+ const skill = typeof path === "string" ? skillsByPath.get(path) : void 0;
5855
+ if (skill !== void 0) reads.push({
5856
+ index,
5857
+ ...skill
5858
+ });
5859
+ });
5860
+ return reads;
5861
+ }
5862
+ /** Return a request tool's name, whether it is a tool instance or a provider object. */
5863
+ function toolName(tool) {
5864
+ if (isOpenAITool(tool)) return tool.function.name;
5865
+ return typeof tool.name === "string" ? tool.name : void 0;
5866
+ }
5867
+ /**
5868
+ * Disclose the tools the read skills' include names produce to one model call.
5869
+ *
5870
+ * Returns the request to send, and the record of the skill tools it
5871
+ * discloses, for the tool-time gate.
5872
+ */
5873
+ async function discloseSkillTools(request, reads, resolver) {
5874
+ const requestTools = /* @__PURE__ */ new Map();
5875
+ for (const tool of request.tools) {
5876
+ const name = toolName(tool);
5877
+ if (name !== void 0) requestTools.set(name, tool);
5878
+ }
5879
+ const names = [...new Set(reads.flatMap((read) => read.includeNames.filter((name) => !requestTools.has(name))))];
5880
+ const tools = await Promise.all(names.map((name) => callSkillToolResolver(resolver, name, request.runtime)));
5881
+ const disclosed = planDisclosure(reads, requestTools, new Map(names.map((name, i) => [name, tools[i]])));
5882
+ if (disclosed.size === 0) return {
5883
+ request,
5884
+ record: {}
5885
+ };
5886
+ const chatModel = await resolveChatModel(request.model, request.runtime);
5887
+ if (llmType(chatModel) === "anthropic") withholdRootCombinatorTools(disclosed);
5888
+ const gatedNames = [...disclosed.keys()].filter((name) => disclosed.get(name).includeName !== void 0).sort();
5889
+ const record = Object.fromEntries(gatedNames.map((name) => [name, disclosed.get(name).includeName]));
5890
+ if (disclosed.size === 0) return {
5891
+ request,
5892
+ record
5893
+ };
5894
+ const build = inlineBlockBuilder(chatModel);
5895
+ if (build !== void 0) return {
5896
+ request: {
5897
+ ...request,
5898
+ messages: discloseInMessages(request.messages, disclosed, build)
5899
+ },
5900
+ record
5901
+ };
5902
+ return {
5903
+ request: {
5904
+ ...request,
5905
+ tools: discloseInTools(request.tools, disclosed, gatedNames)
5906
+ },
5907
+ record
5908
+ };
5909
+ }
5910
+ /**
5911
+ * Classify what each read skill's include names produce, anchoring each tool
5912
+ * at the earliest read producing it.
5913
+ *
5914
+ * An include name that matches a request tool's name produces that tool; any
5915
+ * other produces what the resolver returned for it. A produced tool whose name
5916
+ * a request tool has wins as that request tool, because it's the one that
5917
+ * runs: a deferred one is disclosed early, and a bound one adds nothing, since
5918
+ * the model already sees it. Tools are matched by name, never by identity,
5919
+ * because outer middleware such as tool search replaces the request's tools
5920
+ * with copies.
5921
+ */
5922
+ function planDisclosure(reads, requestTools, resolved) {
5923
+ const disclosed = /* @__PURE__ */ new Map();
5924
+ const discloseIfDeferred = (name, anchor) => {
5925
+ const entry = requestTools.get(name);
5926
+ if (!disclosed.has(name) && isLangChainTool(entry) && entry.extras?.[DEFER_LOADING] === true) disclosed.set(name, {
5927
+ tool: entry,
5928
+ anchor
5929
+ });
5930
+ };
5931
+ for (const { index, includeNames } of reads) for (const includeName of includeNames) {
5932
+ if (requestTools.has(includeName)) {
5933
+ discloseIfDeferred(includeName, index);
5934
+ continue;
5935
+ }
5936
+ for (const tool of resolved.get(includeName) ?? []) {
5937
+ if (disclosed.has(tool.name)) continue;
5938
+ if (requestTools.has(tool.name)) discloseIfDeferred(tool.name, index);
5939
+ else disclosed.set(tool.name, {
5940
+ tool,
5941
+ anchor: index,
5942
+ includeName
5943
+ });
5944
+ }
5945
+ }
5946
+ return disclosed;
5947
+ }
5948
+ /**
5949
+ * Withhold each disclosed tool whose root input schema uses `oneOf`, `anyOf`
5950
+ * or `allOf`, which the Anthropic API rejects, failing the whole request, in
5951
+ * `tools` and inline alike. Withholding it on every path keeps the record
5952
+ * equal to what the model was shown.
5953
+ */
5954
+ function withholdRootCombinatorTools(disclosed) {
5955
+ for (const [name, { tool }] of disclosed) {
5956
+ const schema = anthropicDefinition(tool).input_schema;
5957
+ const keys = ANTHROPIC_ROOT_COMBINATORS.filter((key) => typeof schema === "object" && schema !== null && Object.prototype.hasOwnProperty.call(schema, key));
5958
+ if (keys.length > 0) {
5959
+ console.warn(`Not disclosing tool '${name}': its input_schema has a top-level ${keys.join("/")}, which the Anthropic API does not support`);
5960
+ disclosed.delete(name);
5961
+ }
5962
+ }
5963
+ }
5964
+ /**
5965
+ * Insert one `SystemMessage` per insertion point, carrying the tools anchored
5966
+ * there, for models that accept tool definitions mid-conversation.
5967
+ *
5968
+ * Each message lands at the same index with the same bytes on every call while
5969
+ * its anchor stays visible, so the provider's cached prefix survives.
5970
+ */
5971
+ function discloseInMessages(messages, disclosed, build) {
5972
+ const namesByIndex = /* @__PURE__ */ new Map();
5973
+ for (const [name, { anchor }] of disclosed) {
5974
+ let index = anchor + 1;
5975
+ while (index < messages.length) {
5976
+ const message = messages[index];
5977
+ const emptyReply = AIMessage$1.isInstance(message) && message.content.length === 0 && !message.tool_calls?.length;
5978
+ if (!ToolMessage$1.isInstance(message) && !HumanMessage$1.isInstance(message) && !emptyReply) break;
5979
+ index += 1;
5980
+ }
5981
+ namesByIndex.set(index, [...namesByIndex.get(index) ?? [], name]);
5982
+ }
5983
+ const result = [...messages];
5984
+ for (const index of [...namesByIndex.keys()].sort((a, b) => b - a)) {
5985
+ const blocks = namesByIndex.get(index).sort().map((name) => build(disclosed.get(name).tool));
5986
+ result.splice(index, 0, new SystemMessage$1({ content: blocks }));
5987
+ }
5988
+ return result;
5989
+ }
5990
+ /**
5991
+ * Return `requestTools` with deferred disclosures undeferred and skill tools
5992
+ * appended, for models that can't take tool definitions mid-conversation.
5993
+ *
5994
+ * A deferred tool is replaced by a plain spec rather than a copy: `createAgent`
5995
+ * rejects a request that swaps a registered tool for another instance of the
5996
+ * same name, and doesn't validate plain objects.
5997
+ */
5998
+ function discloseInTools(requestTools, disclosed, gatedNames) {
5999
+ return [...requestTools.map((tool) => {
6000
+ const name = toolName(tool);
6001
+ const entry = name === void 0 ? void 0 : disclosed.get(name);
6002
+ if (entry?.tool !== tool || entry.includeName !== void 0) return tool;
6003
+ const { description, schema, extras = {} } = entry.tool;
6004
+ const { [DEFER_LOADING]: _deferLoading, ...rest } = extras;
6005
+ return {
6006
+ name,
6007
+ description,
6008
+ schema,
6009
+ extras: rest
6010
+ };
6011
+ }), ...gatedNames.map((name) => disclosed.get(name).tool)];
6012
+ }
6013
+ /** Return the model inside a `bind()` / `withConfig()` wrapper; chained calls merge into one. */
6014
+ function unwrapBinding(model) {
6015
+ return RunnableBinding.isRunnableBinding(model) ? model.bound : model;
6016
+ }
6017
+ /**
6018
+ * Return the concrete chat model a request will call.
6019
+ *
6020
+ * Resolves a `ConfigurableModel`, which a string `model` always becomes, to
6021
+ * the instance it will call with this run's `configurable`. Without
6022
+ * configurable overrides that's the instance cached at creation: the instance
6023
+ * cache is keyed on the whole config, so passing `configurable`
6024
+ * unconditionally would build one per thread.
6025
+ */
6026
+ async function resolveChatModel(model, runtime) {
6027
+ const current = unwrapBinding(model);
6028
+ if (current?._queuedMethodOperations === void 0 || typeof current._getModelInstance !== "function") return current;
6029
+ const configurable = runtime.configurable ?? {};
6030
+ const overrides = current._modelParams?.({ configurable }) ?? {};
6031
+ return unwrapBinding(Object.keys(overrides).length > 0 ? await current._getModelInstance({ configurable }) : await current._getModelInstance());
6032
+ }
6033
+ /** Return a chat model's `_llmType()`, which tells providers apart. */
6034
+ function llmType(model) {
6035
+ const fn = model?._llmType;
6036
+ return typeof fn === "function" ? fn.call(model) : void 0;
6037
+ }
6038
+ /**
6039
+ * Return how `chatModel` is given a tool mid-conversation, or `undefined` if
6040
+ * it can't be.
6041
+ *
6042
+ * The one place that decides support, so a model-profile capability can
6043
+ * replace the allowlists later.
6044
+ */
6045
+ function inlineBlockBuilder(chatModel) {
6046
+ const { model: name, useResponsesApi } = chatModel;
6047
+ if (typeof name !== "string") return void 0;
6048
+ const type = llmType(chatModel);
6049
+ if (type === "anthropic" && ANTHROPIC_INLINE_TOOL_MODELS.some((prefix) => name.startsWith(prefix))) return anthropicToolAddition;
6050
+ if (type === "openai" && useResponsesApi === true && OPENAI_INLINE_TOOL_MODELS.some((model) => name === model || name.startsWith(`${model}-`))) return openaiAdditionalTools;
6051
+ }
6052
+ /**
6053
+ * Return the definition `ChatAnthropic` sends in `tools` for `tool`, without
6054
+ * `defer_loading` (a deferred definition would stay withheld) or
6055
+ * `cache_control` (a stray marker uses up a breakpoint).
6056
+ */
6057
+ function anthropicDefinition(tool) {
6058
+ const definition = {
6059
+ name: tool.name,
6060
+ description: tool.description,
6061
+ input_schema: isInteropZodSchema(tool.schema) ? toJsonSchema(tool.schema) : tool.schema
6062
+ };
6063
+ for (const key of ANTHROPIC_DEFINITION_EXTRAS) if (tool.extras?.[key] !== void 0) definition[key] = tool.extras[key];
6064
+ return definition;
6065
+ }
6066
+ /** Build the Anthropic `tool_addition` block carrying `tool`'s full definition. */
6067
+ function anthropicToolAddition(tool) {
6068
+ return {
6069
+ type: "tool_addition",
6070
+ tool: {
6071
+ type: "tool_definition",
6072
+ definition: anthropicDefinition(tool)
6073
+ }
6074
+ };
6075
+ }
6076
+ /** Build the OpenAI Responses `additional_tools` item carrying `tool`'s function schema. */
6077
+ function openaiAdditionalTools(tool) {
6078
+ return {
6079
+ type: "additional_tools",
6080
+ role: "developer",
6081
+ tools: [{
6082
+ type: "function",
6083
+ ...convertToOpenAIFunction(tool)
6084
+ }]
6085
+ };
6086
+ }
5517
6087
  /**
5518
6088
  * Create backend-agnostic middleware for loading and exposing agent skills.
5519
6089
  *
@@ -5537,8 +6107,62 @@ function validateModulePath(raw) {
5537
6107
  * invalidate from any hook — including mid-run, from `afterModel` — and see
5538
6108
  * the fresh list on the following call. See {@link skillsMetadataValue}.
5539
6109
  *
6110
+ * To guarantee the model gets a skill's instructions, for example when the
6111
+ * user names it, pass its name in `pinnedSkills`. Before the next model call,
6112
+ * each named skill's `SKILL.md`, without its frontmatter, is appended to the
6113
+ * conversation as its own `HumanMessage`, with `additional_kwargs.lc_source`
6114
+ * set to `"pinned_skill"` and the skill's `name`, `path` and `description`
6115
+ * under `additional_kwargs.skill`:
6116
+ *
6117
+ * ```ts
6118
+ * await agent.invoke(
6119
+ * {
6120
+ * messages: [new HumanMessage("/write-tests /house-style for auth.py")],
6121
+ * pinnedSkills: ["write-tests", "house-style"],
6122
+ * },
6123
+ * config,
6124
+ * );
6125
+ * ```
6126
+ *
6127
+ * Pins resolve against the skills loaded for the thread, so reset
6128
+ * `skillsMetadata` in the same input to pin a skill added since. See
6129
+ * {@link pinnedSkillsValue}.
6130
+ *
6131
+ * ## Skill tools
6132
+ *
6133
+ * A skill can list the tools its instructions use, separated by spaces, under
6134
+ * `metadata.include_tools` in its `SKILL.md` frontmatter:
6135
+ *
6136
+ * ```yaml
6137
+ * metadata:
6138
+ * include_tools: create_customer_request list_customer_requests
6139
+ * ```
6140
+ *
6141
+ * Pass those tools as `tools`, either as an array or as a
6142
+ * {@link SkillToolResolver} that looks them up by name when a skill is read
6143
+ * or pinned, so one name can stand for a family of tools whose real names are
6144
+ * only known at runtime. The model sees a skill tool only after it reads, with
6145
+ * `read_file`, a skill that lists it, or the skill is pinned, and only while
6146
+ * that read or pin stays in the conversation it is sent; compaction that drops
6147
+ * the read or pin withdraws the tool.
6148
+ * Until then, calling the tool fails with the standard invalid-tool error and
6149
+ * the tool doesn't run. This controls what is in the model's context; it is
6150
+ * not a security boundary, since the model can read any `SKILL.md` at any
6151
+ * time.
6152
+ *
6153
+ * `include_tools` can also name a tool passed to the agent rather than to this
6154
+ * middleware. That tool wins over a skill tool of the same name. If it is
6155
+ * deferred (`extras: { defer_loading: true }`), reading or pinning the skill
6156
+ * discloses it early, and it stays deferred and searchable; if it is bound,
6157
+ * nothing changes.
6158
+ *
6159
+ *
5540
6160
  * @param options - Configuration options
5541
6161
  * @returns AgentMiddleware for skills loading and injection
6162
+ * @throws {ConfigurationError} If `tools` is neither an array nor a
6163
+ * function (`SKILL_TOOLS_UNSUPPORTED_TYPE`), an entry isn't a client tool
6164
+ * (`SKILL_TOOLS_UNSUPPORTED_TYPE`), or two entries share a name
6165
+ * (`SKILL_TOOLS_DUPLICATE_NAME`).
5542
6166
  *
5543
6167
  * @example
5544
6168
  * ```typescript
@@ -5550,30 +6174,60 @@ function validateModulePath(raw) {
5550
6174
  */
5551
6175
  function createSkillsMiddleware(options) {
5552
6176
  const { backend, sources } = options;
6177
+ const skillToolResolver = toSkillToolResolver(options.tools);
6178
+ const hasSkillTools = typeof options.tools === "function" || Array.isArray(options.tools) && options.tools.length > 0;
5553
6179
  return createMiddleware({
5554
6180
  name: "SkillsMiddleware",
5555
6181
  stateSchema: SkillsStateSchema,
5556
6182
  async beforeModel(state) {
5557
- if (state.skillsMetadata !== null && state.skillsMetadata !== void 0) return;
6183
+ const needsLoad = state.skillsMetadata === null || state.skillsMetadata === void 0;
6184
+ const pinnedNames = state.pinnedSkills ?? [];
6185
+ if (!needsLoad && pinnedNames.length === 0) return;
5558
6186
  const resolvedBackend = await resolveBackend(backend, { state });
5559
- const allSkills = /* @__PURE__ */ new Map();
5560
- for (const sourcePath of sources) try {
5561
- const skills = await listSkillsFromBackend(resolvedBackend, sourcePath);
5562
- for (const skill of skills) allSkills.set(skill.name, skill);
5563
- } catch (error) {
5564
- console.debug(`[BackendSkillsMiddleware] Failed to load skills from ${sourcePath}:`, error);
5565
- }
5566
- return { skillsMetadata: Array.from(allSkills.values()) };
6187
+ const skillsMetadata = state.skillsMetadata ?? await loadSkills(resolvedBackend, sources);
6188
+ if (pinnedNames.length === 0) return { skillsMetadata };
6189
+ const messages = await buildPinnedSkillMessages(resolvedBackend, pinnedNames, skillsMetadata);
6190
+ return {
6191
+ ...needsLoad && { skillsMetadata },
6192
+ ...messages.length > 0 && { messages },
6193
+ pinnedSkills: new Overwrite([])
6194
+ };
5567
6195
  },
5568
- wrapModelCall(request, handler) {
6196
+ async wrapModelCall(request, handler) {
5569
6197
  const skillsMetadata = request.state?.skillsMetadata || [];
5570
6198
  const skillsLocations = formatSkillsLocations(sources);
5571
6199
  const skillsList = formatSkillsList(skillsMetadata, sources);
5572
6200
  const skillsSection = SKILLS_SYSTEM_PROMPT.replace("{skills_locations}", skillsLocations).replace("{skills_list}", skillsList);
5573
6201
  const newSystemMessage = request.systemMessage.concat(skillsSection);
5574
- return handler({
6202
+ const prompted = {
5575
6203
  ...request,
5576
6204
  systemMessage: newSystemMessage
6205
+ };
6206
+ const reads = findSkillReads(request.messages, skillsMetadata);
6207
+ const { request: disclosed, record } = reads.length === 0 ? {
6208
+ request: prompted,
6209
+ record: {}
6210
+ } : await discloseSkillTools(prompted, reads, skillToolResolver);
6211
+ const response = await handler(disclosed);
6212
+ const stored = request.state._skillToolsDisclosed;
6213
+ if (!hasSkillTools && Object.keys(stored ?? {}).length === 0) return response;
6214
+ if (typeof response === "object" && response !== null && "structuredResponse" in response) return response;
6215
+ return new Command({ update: { _skillToolsDisclosed: record } });
6216
+ },
6217
+ async wrapToolCall(request, handler) {
6218
+ if (request.tool !== void 0) return handler(request);
6219
+ const name = request.toolCall.name;
6220
+ const record = request.state._skillToolsDisclosed ?? {};
6221
+ const includeName = Object.hasOwn(record, name) ? record[name] : void 0;
6222
+ if (includeName === void 0) return handler(request);
6223
+ const tool = (await callSkillToolResolver(skillToolResolver, includeName, request.runtime)).find((candidate) => candidate.name === name);
6224
+ if (tool === void 0) {
6225
+ console.warn(`Skill tool '${name}' was disclosed via '${includeName}', but the resolver no longer returns it`);
6226
+ return handler(request);
6227
+ }
6228
+ return handler({
6229
+ ...request,
6230
+ tool
5577
6231
  });
5578
6232
  }
5579
6233
  });
@@ -6343,46 +6997,6 @@ function createAsyncSubAgentMiddleware(options) {
6343
6997
  });
6344
6998
  }
6345
6999
  //#endregion
6346
- //#region src/errors.ts
6347
- const CONFIGURATION_ERROR_SYMBOL = Symbol.for("deepagents.configuration_error");
6348
- /**
6349
- * Thrown when `createDeepAgent` receives invalid configuration.
6350
- *
6351
- * Follows the same pattern as {@link SandboxError}: a human-readable
6352
- * `message`, a structured `code` for programmatic handling, and a
6353
- * static `isInstance` guard that works across realms.
6354
- *
6355
- * @example
6356
- * ```typescript
6357
- * try {
6358
- * createDeepAgent({ tools: [myTool] });
6359
- * } catch (error) {
6360
- * if (ConfigurationError.isInstance(error)) {
6361
- * switch (error.code) {
6362
- * case "TOOL_NAME_COLLISION":
6363
- * console.error("Rename your tool:", error.message);
6364
- * break;
6365
- * }
6366
- * }
6367
- * }
6368
- * ```
6369
- */
6370
- var ConfigurationError = class ConfigurationError extends Error {
6371
- code;
6372
- cause;
6373
- [CONFIGURATION_ERROR_SYMBOL] = true;
6374
- name = "ConfigurationError";
6375
- constructor(message, code, cause) {
6376
- super(message);
6377
- this.code = code;
6378
- this.cause = cause;
6379
- Object.setPrototypeOf(this, ConfigurationError.prototype);
6380
- }
6381
- static isInstance(error) {
6382
- return typeof error === "object" && error !== null && error[CONFIGURATION_ERROR_SYMBOL] === true;
6383
- }
6384
- };
6385
- //#endregion
6386
7000
  //#region src/middleware/cache.ts
6387
7001
  /**
6388
7002
  * Creates a middleware that places a cache breakpoint at the end of the static
@@ -7248,13 +7862,10 @@ function createDeepAgent(params = {}) {
7248
7862
  harnessProfile.systemPromptSuffix
7249
7863
  ]);
7250
7864
  /**
7251
- * Process subagents to add SkillsMiddleware for those with their own skills.
7252
- *
7253
- * Custom subagents do NOT inherit skills from the main agent by default.
7254
- * Only the general-purpose subagent inherits the main agent's skills.
7255
- * If a custom subagent needs skills, it must specify its own `skills` array.
7865
+ * Build the default head of a declarative subagent's middleware stack.
7866
+ * Skills go in the tail instead (see buildSubagentMiddleware).
7256
7867
  */
7257
- const createSubagentDefaultMiddleware = (input, subagentProfile, forked) => {
7868
+ const createSubagentDefaultMiddleware = (input, subagentProfile) => {
7258
7869
  const effectivePermissions = input.permissions ?? permissions;
7259
7870
  return [
7260
7871
  createFilesystemMiddleware({
@@ -7263,23 +7874,33 @@ function createDeepAgent(params = {}) {
7263
7874
  tools: computeProfileFilesystemTools(subagentProfile)
7264
7875
  }),
7265
7876
  createSummarizationMiddleware({ backend }),
7266
- createPatchToolCallsMiddleware(),
7267
- ...!forked && input.skills != null && input.skills.length > 0 ? [createSkillsMiddleware({
7268
- backend,
7269
- sources: input.skills
7270
- })] : []
7877
+ createPatchToolCallsMiddleware()
7271
7878
  ];
7272
7879
  };
7273
- const buildSubagentMiddleware = (input) => {
7880
+ /**
7881
+ * The skills middleware for a stack: the last `SkillsMiddleware` among its
7882
+ * custom middleware, else one built from `sources`, else none. A custom one
7883
+ * is picked here rather than left to replace the default by name, because
7884
+ * without `sources` there is no default to replace and it would run with
7885
+ * the novel middleware instead.
7886
+ */
7887
+ const resolveSkillsMiddleware = (sources, custom) => {
7888
+ const passed = custom.findLast((entry) => entry.name === "SkillsMiddleware");
7889
+ if (passed !== void 0) return [passed];
7890
+ return sources != null && sources.length > 0 ? [createSkillsMiddleware({
7891
+ backend,
7892
+ sources
7893
+ })] : [];
7894
+ };
7895
+ const buildSubagentMiddleware = (input, generalPurpose = false) => {
7274
7896
  const subagentProfile = resolveSubagentProfile(input.model);
7275
7897
  const forked = isForkedSubAgent(input);
7276
- const subagentDefaultMiddleware = createSubagentDefaultMiddleware(input, subagentProfile, forked);
7277
- if (forked && skills != null && skills.length > 0) subagentDefaultMiddleware.unshift(createSkillsMiddleware({
7278
- backend,
7279
- sources: skills
7280
- }));
7281
- let subagentMiddleware = mergeMiddlewareStack(subagentDefaultMiddleware, forked && customMiddleware.length > 0 ? mergeMiddleware$1(customMiddleware, input.middleware ?? []) : input.middleware ?? [], [
7898
+ const subagentDefaultMiddleware = createSubagentDefaultMiddleware(input, subagentProfile);
7899
+ const inputMiddleware = forked && customMiddleware.length > 0 ? mergeMiddleware$1(customMiddleware, input.middleware ?? []) : input.middleware ?? [];
7900
+ const subagentSkillsMiddleware = generalPurpose ? resolveSkillsMiddleware(skills, customMiddleware) : resolveSkillsMiddleware(forked ? skills : input.skills, inputMiddleware);
7901
+ let subagentMiddleware = mergeMiddlewareStack(subagentDefaultMiddleware, inputMiddleware, [
7282
7902
  ...resolveMiddleware(subagentProfile.extraMiddleware),
7903
+ ...subagentSkillsMiddleware,
7283
7904
  ...cacheMiddleware,
7284
7905
  ...forked && memory != null && memory.length > 0 ? [createMemoryMiddleware({
7285
7906
  backend,
@@ -7292,9 +7913,9 @@ function createDeepAgent(params = {}) {
7292
7913
  if (!subagentProfile.excludedMiddleware.has("UnsupportedContentMiddleware") && !subagentMiddleware.some((middleware) => middleware.name === "UnsupportedContentMiddleware")) subagentMiddleware.push(createUnsupportedContentMiddleware());
7293
7914
  return subagentMiddleware;
7294
7915
  };
7295
- const normalizeSubagentSpec = (input) => ({
7916
+ const normalizeSubagentSpec = (input, generalPurpose = false) => ({
7296
7917
  ...input,
7297
- middleware: buildSubagentMiddleware(input)
7918
+ middleware: buildSubagentMiddleware(input, generalPurpose)
7298
7919
  });
7299
7920
  const allSubagents = subagents;
7300
7921
  const asyncSubAgents = allSubagents.filter((item) => isAsyncSubAgent(item));
@@ -7307,16 +7928,12 @@ function createDeepAgent(params = {}) {
7307
7928
  description: gpConfig?.description ?? GENERAL_PURPOSE_SUBAGENT.description,
7308
7929
  systemPrompt: gpSystemPrompt,
7309
7930
  model,
7310
- skills,
7311
7931
  tools: effectiveTools
7312
- });
7932
+ }, true);
7313
7933
  generalPurposeSpec.middleware = mergeMiddlewareStack(generalPurposeSpec.middleware ?? [], customMiddleware, [], { appendNew: false });
7314
7934
  inlineSubagents.unshift(generalPurposeSpec);
7315
7935
  }
7316
- const skillsMiddleware = skills != null && skills.length > 0 ? [createSkillsMiddleware({
7317
- backend,
7318
- sources: skills
7319
- })] : [];
7936
+ const skillsMiddleware = resolveSkillsMiddleware(skills, customMiddleware);
7320
7937
  const [fsMiddleware, subagentMiddleware, summarizationMiddleware, patchToolCallsMiddleware] = [
7321
7938
  createFilesystemMiddleware({
7322
7939
  backend,
@@ -7335,7 +7952,6 @@ function createDeepAgent(params = {}) {
7335
7952
  createPatchToolCallsMiddleware()
7336
7953
  ];
7337
7954
  let middleware = mergeMiddlewareStack([
7338
- ...skillsMiddleware,
7339
7955
  fsMiddleware,
7340
7956
  subagentMiddleware,
7341
7957
  summarizationMiddleware,
@@ -7343,6 +7959,7 @@ function createDeepAgent(params = {}) {
7343
7959
  ...asyncSubAgents.length > 0 ? [createAsyncSubAgentMiddleware({ asyncSubAgents })] : []
7344
7960
  ], customMiddleware, [
7345
7961
  ...resolveMiddleware(harnessProfile.extraMiddleware),
7962
+ ...skillsMiddleware,
7346
7963
  ...cacheMiddleware,
7347
7964
  ...memoryMiddleware,
7348
7965
  ...interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []
@@ -9470,6 +10087,6 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
9470
10087
  }
9471
10088
  };
9472
10089
  //#endregion
9473
- export { checkEmptyContent as $, createPatchToolCallsMiddleware as A, CompositeBackend as B, MAX_SKILL_DESCRIPTION_LENGTH as C, skillsMetadataValue as D, createSkillsMiddleware as E, createSubAgent as F, StateBackend as G, createUnsupportedContentMiddleware as H, createSubAgentMiddleware as I, isSandboxBackend as J, SandboxError as K, computeSummarizationDefaults as L, DEFAULT_SUBAGENT_PROMPT as M, GENERAL_PURPOSE_SUBAGENT as N, createMemoryMiddleware as O, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as P, adaptSandboxProtocol as Q, createSummarizationMiddleware as R, createCompletionCallbackMiddleware as S, MAX_SKILL_NAME_LENGTH as T, multimodalBlockSupported as U, MULTIMODAL_BLOCK_TYPES as V, scrubUnsupportedMultimodalContent as W, resolveBackend as X, isSandboxProtocol as Y, adaptBackendProtocol as Z, createHarnessProfile as _, ASYNC_TASK_SYSTEM_PROMPT as a, createAsyncSubAgentMiddleware as b, TASK_SYSTEM_PROMPT as c, registerHarnessProfile as d, getMimeType as et, generalPurposeSubagentConfigSchema as f, EMPTY_HARNESS_PROFILE as g, serializeProfile as h, StoreBackend as i, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as j, filesValue as k, createDeepAgent as l, parseHarnessProfileConfig as m, BaseSandbox as n, normalizeReadPagination as nt, BASE_AGENT_PROMPT as o, harnessProfileConfigSchema as p, applyGrepMaxCount as q, ContextHubBackend as r, performStringReplacement as rt, EXECUTION_SYSTEM_PROMPT as s, LangSmithSandbox as t, isTextMimeType as tt, getHarnessProfile as u, REQUIRED_MIDDLEWARE_NAMES as v, MAX_SKILL_FILE_SIZE as w, isAsyncSubAgent as x, ConfigurationError as y, createFilesystemMiddleware as z };
10090
+ export { adaptSandboxProtocol as $, filesValue as A, createFilesystemMiddleware as B, MAX_SKILL_FILE_SIZE as C, skillsMetadataValue as D, pinnedSkillsValue as E, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as F, scrubUnsupportedMultimodalContent as G, MULTIMODAL_BLOCK_TYPES as H, createSubAgent as I, applyGrepMaxCount as J, StateBackend as K, createSubAgentMiddleware as L, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as M, DEFAULT_SUBAGENT_PROMPT as N, ConfigurationError as O, GENERAL_PURPOSE_SUBAGENT as P, adaptBackendProtocol as Q, computeSummarizationDefaults as R, MAX_SKILL_DESCRIPTION_LENGTH as S, createSkillsMiddleware as T, createUnsupportedContentMiddleware as U, CompositeBackend as V, multimodalBlockSupported as W, isSandboxProtocol as X, isSandboxBackend as Y, resolveBackend as Z, createHarnessProfile as _, ASYNC_TASK_SYSTEM_PROMPT as a, setRealpath as at, isAsyncSubAgent as b, TASK_SYSTEM_PROMPT as c, registerHarnessProfile as d, checkEmptyContent as et, generalPurposeSubagentConfigSchema as f, EMPTY_HARNESS_PROFILE as g, serializeProfile as h, StoreBackend as i, performStringReplacement as it, createPatchToolCallsMiddleware as j, createMemoryMiddleware as k, createDeepAgent as l, parseHarnessProfileConfig as m, BaseSandbox as n, isTextMimeType as nt, BASE_AGENT_PROMPT as o, harnessProfileConfigSchema as p, SandboxError as q, ContextHubBackend as r, normalizeReadPagination as rt, EXECUTION_SYSTEM_PROMPT as s, LangSmithSandbox as t, getMimeType as tt, getHarnessProfile as u, REQUIRED_MIDDLEWARE_NAMES as v, MAX_SKILL_NAME_LENGTH as w, createCompletionCallbackMiddleware as x, createAsyncSubAgentMiddleware as y, createSummarizationMiddleware as z };
9474
10091
 
9475
- //# sourceMappingURL=langsmith--RbtjNdQ.js.map
10092
+ //# sourceMappingURL=langsmith-v_uskil7.js.map