Skip to content

Commit 90ade66

Browse files
fix(deepagents): fix type issues (#174)
* fix(deepagents): fix type issues * fix types * minor tweak
1 parent 7038d24 commit 90ade66

File tree

9 files changed

+163
-101
lines changed

9 files changed

+163
-101
lines changed

libs/deepagents/src/agent.test-d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import type {
2525
InferCompiledSubagents,
2626
InferRegularSubagents,
2727
} from "./types.js";
28-
import type { FileData } from "./backends/protocol.js";
28+
import type { FilesRecordUpdate } from "./middleware/fs.js";
2929

3030
// Test middleware with research state
3131
const ResearchStateSchema = z.object({
@@ -134,7 +134,7 @@ describe("createDeepAgent types", () => {
134134
expectTypeOf(result.counter).toEqualTypeOf<number>();
135135
// should have built-in state
136136
expectTypeOf(result).toHaveProperty("files");
137-
expectTypeOf(result.files).toEqualTypeOf<Record<string, FileData>>();
137+
expectTypeOf(result.files).toEqualTypeOf<FilesRecordUpdate | undefined>();
138138
expectTypeOf(result).toHaveProperty("todos");
139139
expectTypeOf(result.todos).toEqualTypeOf<
140140
{

libs/deepagents/src/agent.ts

Lines changed: 94 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ export function createDeepAgent<
111111
skills,
112112
} = params;
113113

114-
// Combine system prompt with base prompt like Python implementation
114+
/**
115+
* Combine system prompt with base prompt like Python implementation
116+
*/
115117
const finalSystemPrompt = systemPrompt
116118
? typeof systemPrompt === "string"
117119
? `${systemPrompt}\n\n${BASE_PROMPT}`
@@ -128,15 +130,19 @@ export function createDeepAgent<
128130
})
129131
: BASE_PROMPT;
130132

131-
// Create backend configuration for filesystem middleware
132-
// If no backend is provided, use a factory that creates a StateBackend
133+
/**
134+
* Create backend configuration for filesystem middleware
135+
* If no backend is provided, use a factory that creates a StateBackend
136+
*/
133137
const filesystemBackend = backend
134138
? backend
135139
: (config: { state: unknown; store?: BaseStore }) =>
136140
new StateBackend(config);
137141

138-
// Add skills middleware if skill sources provided
139-
const skillsMiddleware =
142+
/**
143+
* Skills middleware (created conditionally for runtime use)
144+
*/
145+
const skillsMiddlewareArray =
140146
skills != null && skills.length > 0
141147
? [
142148
createSkillsMiddleware({
@@ -146,111 +152,144 @@ export function createDeepAgent<
146152
]
147153
: [];
148154

149-
// Built-in middleware array
155+
/**
156+
* Memory middleware (created conditionally for runtime use)
157+
*/
158+
const memoryMiddlewareArray =
159+
memory != null && memory.length > 0
160+
? [
161+
createMemoryMiddleware({
162+
backend: filesystemBackend,
163+
sources: memory,
164+
}),
165+
]
166+
: [];
167+
168+
/**
169+
* Built-in middleware array - core middleware with known types
170+
* This tuple is typed without conditional spreads to preserve TypeScript's tuple inference.
171+
* Optional middleware (skills, memory, HITL) are handled at runtime but typed explicitly.
172+
*/
150173
const builtInMiddleware = [
151-
// Provides todo list management capabilities for tracking tasks
174+
/**
175+
* Provides todo list management capabilities for tracking tasks
176+
*/
152177
todoListMiddleware(),
153-
// Add skills middleware if skill sources provided
154-
...skillsMiddleware,
155-
// Enables filesystem operations and optional long-term memory storage
178+
/**
179+
* Enables filesystem operations and optional long-term memory storage
180+
*/
156181
createFilesystemMiddleware({ backend: filesystemBackend }),
157-
// Enables delegation to specialized subagents for complex tasks
182+
/**
183+
* Enables delegation to specialized subagents for complex tasks
184+
*/
158185
createSubAgentMiddleware({
159186
defaultModel: model,
160187
defaultTools: tools as StructuredTool[],
161188
defaultMiddleware: [
162-
// Subagent middleware: Todo list management
189+
/**
190+
* Subagent middleware: Todo list management
191+
*/
163192
todoListMiddleware(),
164-
// Subagent middleware: Skills (if provided)
165-
...skillsMiddleware,
166-
// Subagent middleware: Filesystem operations
193+
/**
194+
* Subagent middleware: Skills (if provided) - added at runtime
195+
*/
196+
...skillsMiddlewareArray,
197+
/**
198+
* Subagent middleware: Filesystem operations
199+
*/
167200
createFilesystemMiddleware({
168201
backend: filesystemBackend,
169202
}),
170-
// Subagent middleware: Automatic conversation summarization when token limits are approached
203+
/**
204+
* Subagent middleware: Automatic conversation summarization when token limits are approached
205+
*/
171206
summarizationMiddleware({
172207
model,
173208
trigger: { tokens: 170_000 },
174209
keep: { messages: 6 },
175210
}),
176-
// Subagent middleware: Anthropic prompt caching for improved performance
211+
/**
212+
* Subagent middleware: Anthropic prompt caching for improved performance
213+
*/
177214
anthropicPromptCachingMiddleware({
178215
unsupportedModelBehavior: "ignore",
179216
}),
180-
// Subagent middleware: Patches tool calls for compatibility
217+
/**
218+
* Subagent middleware: Patches tool calls for compatibility
219+
*/
181220
createPatchToolCallsMiddleware(),
182221
],
183222
defaultInterruptOn: interruptOn,
184223
subagents: subagents as unknown as (SubAgent | CompiledSubAgent)[],
185224
generalPurposeAgent: true,
186225
}),
187-
// Automatically summarizes conversation history when token limits are approached
226+
/**
227+
* Automatically summarizes conversation history when token limits are approached
228+
*/
188229
summarizationMiddleware({
189230
model,
190231
trigger: { tokens: 170_000 },
191232
keep: { messages: 6 },
192233
}),
193-
// Enables Anthropic prompt caching for improved performance and reduced costs
234+
/**
235+
* Enables Anthropic prompt caching for improved performance and reduced costs
236+
*/
194237
anthropicPromptCachingMiddleware({
195238
unsupportedModelBehavior: "ignore",
196239
}),
197-
// Patches tool calls to ensure compatibility across different model providers
240+
/**
241+
* Patches tool calls to ensure compatibility across different model providers
242+
*/
198243
createPatchToolCallsMiddleware(),
199-
// Add memory middleware if memory sources provided
200-
...(memory != null && memory.length > 0
201-
? [
202-
createMemoryMiddleware({
203-
backend: filesystemBackend,
204-
sources: memory,
205-
}),
206-
]
207-
: []),
208244
] as const;
209245

210-
// Add human-in-the-loop middleware if interrupt config provided
211-
if (interruptOn) {
212-
// builtInMiddleware is typed as readonly to enable type inference
213-
// however, we need to push to it to add the middleware, so let's ignore the type error
214-
// @ts-expect-error - builtInMiddleware is readonly
215-
builtInMiddleware.push(humanInTheLoopMiddleware({ interruptOn }));
216-
}
217-
218-
// Combine built-in middleware with custom middleware
219-
// The custom middleware is typed as TMiddleware to preserve type information
220-
const allMiddleware = [
246+
/**
247+
* Runtime middleware array: combine built-in + optional middleware
248+
* Note: The type is handled separately via AllMiddleware type alias
249+
*/
250+
const runtimeMiddleware: AgentMiddleware[] = [
221251
...builtInMiddleware,
222-
...(customMiddleware as unknown as TMiddleware),
223-
] as const;
252+
...skillsMiddlewareArray,
253+
...memoryMiddlewareArray,
254+
...(interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []),
255+
...(customMiddleware as unknown as AgentMiddleware[]),
256+
];
224257

225-
// Note: Recursion limit of 1000 (matching Python behavior) should be passed
226-
// at invocation time: agent.invoke(input, { recursionLimit: 1000 })
258+
/**
259+
* Note: Recursion limit of 1000 (matching Python behavior) should be passed
260+
* at invocation time: agent.invoke(input, { recursionLimit: 1000 })
261+
*/
227262
const agent = createAgent({
228263
model,
229264
systemPrompt: finalSystemPrompt,
230265
tools: tools as StructuredTool[],
231-
middleware: allMiddleware as unknown as AgentMiddleware[],
266+
middleware: runtimeMiddleware,
232267
responseFormat: responseFormat as ResponseFormat,
233268
contextSchema,
234269
checkpointer,
235270
store,
236271
name,
237272
});
238273

239-
// Combine custom middleware with flattened subagent middleware for complete type inference
240-
// This ensures InferMiddlewareStates captures state from both sources
274+
/**
275+
* Combine custom middleware with flattened subagent middleware for complete type inference
276+
* This ensures InferMiddlewareStates captures state from both sources
277+
*/
241278
type AllMiddleware = readonly [
242279
...typeof builtInMiddleware,
243280
...TMiddleware,
244281
...FlattenSubAgentMiddleware<TSubagents>,
245282
];
246283

247-
// Return as DeepAgent with proper DeepAgentTypeConfig
248-
// - Response: TResponse (from responseFormat parameter)
249-
// - State: undefined (state comes from middleware)
250-
// - Context: ContextSchema
251-
// - Middleware: AllMiddleware (built-in + custom + subagent middleware for state inference)
252-
// - Tools: TTools
253-
// - Subagents: TSubagents (for type-safe streaming)
284+
/**
285+
* Return as DeepAgent with proper DeepAgentTypeConfig
286+
* - Response: TResponse (from responseFormat parameter)
287+
* - State: undefined (state comes from middleware)
288+
* - Context: ContextSchema
289+
* - Middleware: AllMiddleware (built-in + custom + subagent middleware for state inference)
290+
* - Tools: TTools
291+
* - Subagents: TSubagents (for type-safe streaming)
292+
*/
254293
return agent as unknown as DeepAgent<
255294
DeepAgentTypeConfig<
256295
TResponse,

libs/deepagents/src/backends/protocol.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe("Protocol Types", () => {
9898

9999
describe("isSandboxBackend", () => {
100100
it("should return true for backends with execute function and id string", () => {
101-
const sandboxBackend: SandboxBackendProtocol = {
101+
const sandboxBackend = {
102102
id: "test-sandbox",
103103
execute: async () => ({ output: "", exitCode: 0, truncated: false }),
104104
lsInfo: async () => [],
@@ -109,13 +109,13 @@ describe("isSandboxBackend", () => {
109109
edit: async () => ({ path: "" }),
110110
uploadFiles: async () => [],
111111
downloadFiles: async () => [],
112-
};
112+
} as unknown as SandboxBackendProtocol;
113113

114114
expect(isSandboxBackend(sandboxBackend)).toBe(true);
115115
});
116116

117117
it("should return false for backends without execute", () => {
118-
const nonSandboxBackend: BackendProtocol = {
118+
const nonSandboxBackend = {
119119
lsInfo: async () => [],
120120
read: async () => "",
121121
grepRaw: async () => [],
@@ -124,7 +124,7 @@ describe("isSandboxBackend", () => {
124124
edit: async () => ({ path: "" }),
125125
uploadFiles: async () => [],
126126
downloadFiles: async () => [],
127-
};
127+
} as unknown as BackendProtocol;
128128

129129
expect(isSandboxBackend(nonSandboxBackend)).toBe(false);
130130
});

0 commit comments

Comments
 (0)