deepagents 1.8.1 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <div align="center">
2
2
  <a href="https://docs.langchain.com/oss/python/deepagents/overview#deep-agents-overview">
3
3
  <picture>
4
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg">
5
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-light.svg">
6
- <img alt="Deep Agents Logo" src="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg" width="80%">
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-light.svg">
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg">
6
+ <img alt="Deep Agents Logo" src="https://raw.githubusercontent.com/langchain-ai/deepagentsjs/refs/heads/main/.github/images/logo-dark.svg" width="50%">
7
7
  </picture>
8
8
  </a>
9
9
  </div>
@@ -132,6 +132,9 @@ const researchInstructions = `You are an expert researcher. Your job is to condu
132
132
 
133
133
  You have access to an internet search tool as your primary means of gathering information.
134
134
 
135
+ > [!TIP]
136
+ > For developing, debugging, and deploying AI agents and LLM applications, see [LangSmith](https://docs.langchain.com/langsmith/home).
137
+
135
138
  ## \`internet_search\`
136
139
 
137
140
  Use this to run an internet search for a given query. You can specify the max number of results to return, the topic, and whether raw content should be included.
@@ -713,3 +716,52 @@ const agent = createAgent({
713
716
  ],
714
717
  });
715
718
  ```
719
+
720
+ ## ACP (Agent Client Protocol) Support
721
+
722
+ Deep Agents can be exposed as an [Agent Client Protocol](https://agentclientprotocol.com) server, enabling integration with IDEs like [Zed](https://zed.dev), JetBrains, and other ACP-compatible clients through a standardized JSON-RPC 2.0 protocol over stdio.
723
+
724
+ The `deepagents-acp` package wraps your Deep Agent with ACP support:
725
+
726
+ ```bash
727
+ npm install deepagents-acp
728
+ ```
729
+
730
+ The quickest way to get started is via the CLI:
731
+
732
+ ```bash
733
+ npx deepagents-acp --name my-agent --workspace /path/to/project
734
+ ```
735
+
736
+ Or programmatically:
737
+
738
+ ```typescript
739
+ import { startServer } from "deepagents-acp";
740
+
741
+ await startServer({
742
+ agents: {
743
+ name: "coding-assistant",
744
+ description: "AI coding assistant with filesystem access",
745
+ skills: ["./skills/"],
746
+ },
747
+ workspaceRoot: process.cwd(),
748
+ });
749
+ ```
750
+
751
+ To use with Zed, add the following to your Zed settings:
752
+
753
+ ```json
754
+ {
755
+ "agent": {
756
+ "profiles": {
757
+ "deepagents": {
758
+ "name": "DeepAgents",
759
+ "command": "npx",
760
+ "args": ["deepagents-acp"]
761
+ }
762
+ }
763
+ }
764
+ }
765
+ ```
766
+
767
+ See the [deepagents-acp README](libs/acp/README.md) and the [ACP server example](examples/acp-server/) for full documentation and advanced configuration.
package/dist/index.cjs CHANGED
@@ -125,6 +125,8 @@ var SandboxError = class SandboxError extends Error {
125
125
  const EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
126
126
  const MAX_LINE_LENGTH = 1e4;
127
127
  const LINE_NUMBER_WIDTH = 6;
128
+ const TOOL_RESULT_TOKEN_LIMIT = 2e4;
129
+ const TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
128
130
  /**
129
131
  * Sanitize tool_call_id to prevent path traversal and separator issues.
130
132
  *
@@ -260,6 +262,21 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
260
262
  return [content.split(oldString).join(newString), occurrences];
261
263
  }
262
264
  /**
265
+ * Truncate list or string result if it exceeds token limit (rough estimate: 4 chars/token).
266
+ */
267
+ function truncateIfTooLong(result) {
268
+ if (Array.isArray(result)) {
269
+ const totalChars = result.reduce((sum, item) => sum + item.length, 0);
270
+ if (totalChars > TOOL_RESULT_TOKEN_LIMIT * 4) {
271
+ const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
272
+ return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
273
+ }
274
+ return result;
275
+ }
276
+ if (result.length > TOOL_RESULT_TOKEN_LIMIT * 4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
277
+ return result;
278
+ }
279
+ /**
263
280
  * Validate and normalize a directory path.
264
281
  *
265
282
  * Ensures paths are safe to use by preventing directory traversal attacks
@@ -839,7 +856,9 @@ function createLsTool(backend, options) {
839
856
  const size = info.size ? ` (${info.size} bytes)` : "";
840
857
  lines.push(`${info.path}${size}`);
841
858
  }
842
- return lines.join("\n");
859
+ const result = truncateIfTooLong(lines);
860
+ if (Array.isArray(result)) return result.join("\n");
861
+ return result;
843
862
  }, {
844
863
  name: "ls",
845
864
  description: customDescription || LS_TOOL_DESCRIPTION,
@@ -957,7 +976,9 @@ function createGlobTool(backend, options) {
957
976
  const { pattern, path = "/" } = input;
958
977
  const infos = await resolvedBackend.globInfo(pattern, path);
959
978
  if (infos.length === 0) return `No files found matching pattern '${pattern}'`;
960
- return infos.map((info) => info.path).join("\n");
979
+ const result = truncateIfTooLong(infos.map((info) => info.path));
980
+ if (Array.isArray(result)) return result.join("\n");
981
+ return result;
961
982
  }, {
962
983
  name: "glob",
963
984
  description: customDescription || GLOB_TOOL_DESCRIPTION,
@@ -990,7 +1011,9 @@ function createGrepTool(backend, options) {
990
1011
  }
991
1012
  lines.push(` ${match.line}: ${match.text}`);
992
1013
  }
993
- return lines.join("\n");
1014
+ const truncated = truncateIfTooLong(lines);
1015
+ if (Array.isArray(truncated)) return truncated.join("\n");
1016
+ return truncated;
994
1017
  }, {
995
1018
  name: "grep",
996
1019
  description: customDescription || GREP_TOOL_DESCRIPTION,
@@ -1462,7 +1485,16 @@ function createTaskTool(options) {
1462
1485
  const subagentState = filterStateForSubagent((0, _langchain_langgraph.getCurrentTaskInput)());
1463
1486
  subagentState.messages = [new _langchain_core_messages.HumanMessage({ content: description })];
1464
1487
  const result = await subagent.invoke(subagentState, config);
1465
- if (!config.toolCall?.id) throw new Error("Tool call ID is required for subagent invocation");
1488
+ if (!config.toolCall?.id) {
1489
+ const messages = result.messages;
1490
+ let content = (messages?.[messages.length - 1])?.content || "Task completed";
1491
+ if (Array.isArray(content)) {
1492
+ content = content.filter((block) => !INVALID_TOOL_MESSAGE_BLOCK_TYPES.includes(block.type));
1493
+ if (content.length === 0) return "Task completed";
1494
+ return content.map((block) => "text" in block ? block.text : JSON.stringify(block)).join("\n");
1495
+ }
1496
+ return content;
1497
+ }
1466
1498
  return returnCommandWithStateUpdate(result, config.toolCall.id);
1467
1499
  }, {
1468
1500
  name: "task",
@@ -1799,7 +1831,7 @@ async function loadMemoryFromBackend(backend, path) {
1799
1831
  * ```
1800
1832
  */
1801
1833
  function createMemoryMiddleware(options) {
1802
- const { backend, sources } = options;
1834
+ const { backend, sources, addCacheControl = false } = options;
1803
1835
  /**
1804
1836
  * Resolve backend from instance or factory.
1805
1837
  */
@@ -1824,7 +1856,16 @@ function createMemoryMiddleware(options) {
1824
1856
  },
1825
1857
  wrapModelCall(request, handler) {
1826
1858
  const formattedContents = formatMemoryContents(request.state?.memoryContents || {}, sources);
1827
- const newSystemMessage = new langchain.SystemMessage(MEMORY_SYSTEM_PROMPT.replace("{memory_contents}", formattedContents)).concat(request.systemMessage);
1859
+ const memorySection = MEMORY_SYSTEM_PROMPT.replace("{memory_contents}", formattedContents);
1860
+ const existingContent = request.systemMessage.content;
1861
+ const newSystemMessage = new langchain.SystemMessage({ content: [...typeof existingContent === "string" ? [{
1862
+ type: "text",
1863
+ text: existingContent
1864
+ }] : Array.isArray(existingContent) ? existingContent : [], {
1865
+ type: "text",
1866
+ text: memorySection,
1867
+ ...addCacheControl && { cache_control: { type: "ephemeral" } }
1868
+ }] });
1828
1869
  return handler({
1829
1870
  ...request,
1830
1871
  systemMessage: newSystemMessage
@@ -2835,9 +2876,10 @@ ${summary}
2835
2876
  */
2836
2877
  function isContextOverflow(err) {
2837
2878
  let cause = err;
2838
- while (cause != null) {
2879
+ for (;;) {
2880
+ if (!cause) break;
2839
2881
  if (_langchain_core_errors.ContextOverflowError.isInstance(cause)) return true;
2840
- cause = typeof cause === "object" && cause !== null && "cause" in cause ? cause.cause : void 0;
2882
+ cause = typeof cause === "object" && "cause" in cause ? cause.cause : void 0;
2841
2883
  }
2842
2884
  return false;
2843
2885
  }
@@ -3828,6 +3870,10 @@ var CompositeBackend = class {
3828
3870
  this.routes = routes;
3829
3871
  this.sortedRoutes = Object.entries(routes).sort((a, b) => b[0].length - a[0].length);
3830
3872
  }
3873
+ /** Delegates to default backend's id if it is a sandbox, otherwise empty string. */
3874
+ get id() {
3875
+ return isSandboxBackend(this.default) ? this.default.id : "";
3876
+ }
3831
3877
  /**
3832
3878
  * Determine which backend handles this key and strip prefix.
3833
3879
  *
@@ -4757,10 +4803,66 @@ var BaseSandbox = class {
4757
4803
  }
4758
4804
  };
4759
4805
 
4806
+ //#endregion
4807
+ //#region src/middleware/cache.ts
4808
+ /**
4809
+ * Creates a middleware that places a cache breakpoint at the end of the static
4810
+ * system prompt content.
4811
+ *
4812
+ * This middleware tags the last block of the system message with
4813
+ * `cache_control: { type: "ephemeral" }` at the time it runs, capturing all
4814
+ * static content injected by preceding middleware (e.g. todo list instructions,
4815
+ * filesystem tools, subagent instructions) in a single cache breakpoint.
4816
+ *
4817
+ * This should run after all static system prompt middleware and before any
4818
+ * dynamic middleware (e.g. memory) so the breakpoint sits at the boundary
4819
+ * between stable and changing content.
4820
+ *
4821
+ * When used alongside memory middleware (which adds its own breakpoint on the
4822
+ * memory block), the result is two separate cache breakpoints:
4823
+ * - One covering all static content
4824
+ * - One covering the memory block
4825
+ *
4826
+ * This is a no-op when the system message has no content blocks.
4827
+ */
4828
+ function createCacheBreakpointMiddleware() {
4829
+ return (0, langchain.createMiddleware)({
4830
+ name: "CacheBreakpointMiddleware",
4831
+ wrapModelCall(request, handler) {
4832
+ const existingContent = request.systemMessage.content;
4833
+ const existingBlocks = typeof existingContent === "string" ? [{
4834
+ type: "text",
4835
+ text: existingContent
4836
+ }] : Array.isArray(existingContent) ? [...existingContent] : [];
4837
+ if (existingBlocks.length === 0) return handler(request);
4838
+ existingBlocks[existingBlocks.length - 1] = {
4839
+ ...existingBlocks[existingBlocks.length - 1],
4840
+ cache_control: { type: "ephemeral" }
4841
+ };
4842
+ return handler({
4843
+ ...request,
4844
+ systemMessage: new langchain.SystemMessage({ content: existingBlocks })
4845
+ });
4846
+ }
4847
+ });
4848
+ }
4849
+
4760
4850
  //#endregion
4761
4851
  //#region src/agent.ts
4762
4852
  const BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
4763
4853
  /**
4854
+ * Detect whether a model is an Anthropic model.
4855
+ * Used to gate Anthropic-specific prompt caching optimizations (cache_control breakpoints).
4856
+ */
4857
+ function isAnthropicModel(model) {
4858
+ if (typeof model === "string") {
4859
+ if (model.includes(":")) return model.split(":")[0] === "anthropic";
4860
+ return model.startsWith("claude");
4861
+ }
4862
+ if (model.getName() === "ConfigurableModel") return model._defaultConfig?.modelProvider === "anthropic";
4863
+ return model.getName() === "ChatAnthropic";
4864
+ }
4865
+ /**
4764
4866
  * Create a Deep Agent with middleware-based architecture.
4765
4867
  *
4766
4868
  * Matches Python's create_deep_agent function, using middleware for all features:
@@ -4793,16 +4895,20 @@ const BASE_PROMPT = `In order to complete the objective that the user asks of yo
4793
4895
  */
4794
4896
  function createDeepAgent(params = {}) {
4795
4897
  const { model = "claude-sonnet-4-5-20250929", tools = [], systemPrompt, middleware: customMiddleware = [], subagents = [], responseFormat, contextSchema, checkpointer, store, backend, interruptOn, name, memory, skills } = params;
4796
- /**
4797
- * Combine system prompt with base prompt like Python implementation
4798
- */
4799
- const finalSystemPrompt = systemPrompt ? typeof systemPrompt === "string" ? `${systemPrompt}\n\n${BASE_PROMPT}` : new langchain.SystemMessage({ content: [{
4898
+ const anthropicModel = isAnthropicModel(model);
4899
+ const finalSystemPrompt = new langchain.SystemMessage({ content: systemPrompt ? typeof systemPrompt === "string" ? [{
4900
+ type: "text",
4901
+ text: `${systemPrompt}\n\n${BASE_PROMPT}`
4902
+ }] : [{
4800
4903
  type: "text",
4801
4904
  text: BASE_PROMPT
4802
4905
  }, ...typeof systemPrompt.content === "string" ? [{
4803
4906
  type: "text",
4804
4907
  text: systemPrompt.content
4805
- }] : systemPrompt.content] }) : BASE_PROMPT;
4908
+ }] : systemPrompt.content] : [{
4909
+ type: "text",
4910
+ text: BASE_PROMPT
4911
+ }] });
4806
4912
  /**
4807
4913
  * Create backend configuration for filesystem middleware
4808
4914
  * If no backend is provided, use a factory that creates a StateBackend
@@ -4820,7 +4926,8 @@ function createDeepAgent(params = {}) {
4820
4926
  */
4821
4927
  const memoryMiddlewareArray = memory != null && memory.length > 0 ? [createMemoryMiddleware({
4822
4928
  backend: filesystemBackend,
4823
- sources: memory
4929
+ sources: memory,
4930
+ addCacheControl: anthropicModel
4824
4931
  })] : [];
4825
4932
  /**
4826
4933
  * Process subagents to add SkillsMiddleware for those with their own skills.
@@ -4869,7 +4976,10 @@ function createDeepAgent(params = {}) {
4869
4976
  model,
4870
4977
  backend: filesystemBackend
4871
4978
  }),
4872
- (0, langchain.anthropicPromptCachingMiddleware)({ unsupportedModelBehavior: "ignore" }),
4979
+ (0, langchain.anthropicPromptCachingMiddleware)({
4980
+ unsupportedModelBehavior: "ignore",
4981
+ minMessagesToCache: 1
4982
+ }),
4873
4983
  createPatchToolCallsMiddleware()
4874
4984
  ];
4875
4985
  /**
@@ -4892,8 +5002,12 @@ function createDeepAgent(params = {}) {
4892
5002
  createSubAgentMiddleware({
4893
5003
  defaultModel: model,
4894
5004
  defaultTools: tools,
4895
- defaultMiddleware: subagentMiddleware,
4896
- generalPurposeMiddleware: [...subagentMiddleware, ...skillsMiddlewareArray],
5005
+ defaultMiddleware: [...subagentMiddleware, ...anthropicModel ? [createCacheBreakpointMiddleware()] : []],
5006
+ generalPurposeMiddleware: [
5007
+ ...subagentMiddleware,
5008
+ ...skillsMiddlewareArray,
5009
+ ...anthropicModel ? [createCacheBreakpointMiddleware()] : []
5010
+ ],
4897
5011
  defaultInterruptOn: interruptOn,
4898
5012
  subagents: processedSubagents,
4899
5013
  generalPurposeAgent: true
@@ -4902,10 +5016,14 @@ function createDeepAgent(params = {}) {
4902
5016
  model,
4903
5017
  backend: filesystemBackend
4904
5018
  }),
4905
- (0, langchain.anthropicPromptCachingMiddleware)({ unsupportedModelBehavior: "ignore" }),
5019
+ (0, langchain.anthropicPromptCachingMiddleware)({
5020
+ unsupportedModelBehavior: "ignore",
5021
+ minMessagesToCache: 1
5022
+ }),
4906
5023
  createPatchToolCallsMiddleware()
4907
5024
  ],
4908
5025
  ...skillsMiddlewareArray,
5026
+ ...anthropicModel ? [createCacheBreakpointMiddleware()] : [],
4909
5027
  ...memoryMiddlewareArray,
4910
5028
  ...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : [],
4911
5029
  ...customMiddleware
@@ -4915,7 +5033,10 @@ function createDeepAgent(params = {}) {
4915
5033
  checkpointer,
4916
5034
  store,
4917
5035
  name
4918
- }).withConfig({ recursionLimit: 1e4 });
5036
+ }).withConfig({
5037
+ recursionLimit: 1e4,
5038
+ metadata: { ls_integration: "deepagents" }
5039
+ });
4919
5040
  }
4920
5041
 
4921
5042
  //#endregion