Skip to content

Conversation

@alvedder
Copy link

@alvedder alvedder commented Feb 8, 2026

Adds middleware override capability for DeepAgents and subagents, allowing users to replace specific default middleware by name while preserving the middleware execution order.

Motivation

Previously, middleware arrays were merged through simple concatenation ([...defaults, ...custom]), making it difficult to replace or customize default middleware behavior as langchain's createAgent throws an error if detects multiple same-named middleware.

Users who wanted to modify a specific middleware (e.g., filesystem, skills, memory) had to either:

  • Accept the default behavior entirely, or
  • Manually reconstruct the entire middleware chain

This commit introduces a smarter merge strategy that:

  • Allows overriding default middleware by matching the name property
  • Preserves the position of overridden middleware in the chain
  • Appends new custom middleware at the end (as it was previously)
  • Maintains backward compatibility for users who don't override anything

This gives users fine-grained control over agent behavior without requiring deep knowledge of the internal middleware ordering.

Changes

libs/deepagents/src/middleware/utils.ts

Added the mergeMiddleware function that implements the merge strategy:

  • Creates a map of custom middleware by name for efficient lookups
  • Replaces default middleware in-place when a custom override exists
  • Appends non-overriding custom middleware at the end
  • Returns a new array without mutating inputs

Example behavior:

const defaults = [mw1, mw2, mw3];
const custom = [mw2Override, mw4];
const result = mergeMiddleware(defaults, custom);
// Result: [mw1, mw2Override, mw3, mw4]

libs/deepagents/src/middleware/utils.test.ts

Added test coverage for mergeMiddleware with 10 test cases covering:

  • Empty array handling (both, either, or neither empty)
  • In-place replacement of named middleware
  • Appending new custom middleware
  • Mixed override and addition scenarios
  • Full replacement when all defaults are overridden
  • Order preservation for non-overridden middleware
  • Input array immutability

libs/deepagents/src/agent.ts

Updated createDeepAgent function:

  • Replaced array concatenation with mergeMiddleware for the main agent's runtime middleware
  • Applied the same pattern to subagent skills middleware processing
  • Maintained the same middleware execution order for backward compatibility

Before:

const runtimeMiddleware: AgentMiddleware[] = [
  ...builtInMiddleware,
  ...skillsMiddlewareArray,
  ...memoryMiddlewareArray,
  ...(interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []),
  ...(customMiddleware as unknown as AgentMiddleware[]),
];

After:

const runtimeMiddleware: AgentMiddleware[] = mergeMiddleware(
  [
    ...builtInMiddleware,
    ...skillsMiddlewareArray,
    ...memoryMiddlewareArray,
    ...(interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []),
  ],
  customMiddleware as unknown as AgentMiddleware[],
);

libs/deepagents/src/middleware/subagents.ts

Updated getSubagents function:

  • Replaced array concatenation with mergeMiddleware for subagent middleware
  • Imported the new utility function
  • Consistent behavior with main agent middleware merging

Before:

const middleware = agentParams.middleware
  ? [...defaultSubagentMiddleware, ...agentParams.middleware]
  : [...defaultSubagentMiddleware];

After:

const middleware = agentParams.middleware
  ? mergeMiddleware(defaultSubagentMiddleware, agentParams.middleware)
  : [...defaultSubagentMiddleware];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant