Skip to content

Commit 9ee6a42

Browse files
committed
Don't flatten tool call for direct spawning agents, use same prompt & params as spawn_agents
1 parent f3c479f commit 9ee6a42

File tree

3 files changed

+21
-58
lines changed

3 files changed

+21
-58
lines changed

common/src/tools/params/tool/spawn-agents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The prompt field is a simple string, while params is a JSON object that gets val
3737
3838
Each agent available is already defined as another tool, or, dynamically defined later in the conversation.
3939
40-
You can call agents either as direct tool calls (e.g., \`example-agent\`) or use \`spawn_agents\`. Both formats work, but **prefer using spawn_agents** because it allows you to spawn multiple agents in parallel for better performance. When using direct tool calls, the schema is flat (prompt is a field alongside other params), whereas spawn_agents uses nested \`prompt\` and \`params\` fields.
40+
You can call agents either as direct tool calls (e.g., \`example-agent\`) or use \`spawn_agents\`. Both formats work, but **prefer using spawn_agents** because it allows you to spawn multiple agents in parallel for better performance. Both use the same schema with nested \`prompt\` and \`params\` fields.
4141
4242
**IMPORTANT**: Many agents have REQUIRED fields in their params schema. Check the agent's schema before spawning - if params has required fields, you MUST include them in the params object. For example, code-searcher requires \`searchQueries\`, commander requires \`command\`.
4343

packages/agent-runtime/src/templates/prompts.ts

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,24 @@ export function getAgentShortName(agentType: AgentTemplateType): string {
2020
}
2121

2222
/**
23-
* Builds a flat input schema for an agent tool by combining prompt and params.
24-
* E.g., { prompt?: string, ...paramsFields }
23+
* Builds an input schema for an agent tool with prompt and params as top-level fields.
24+
* This matches the spawn_agents schema structure: { prompt?: string, params?: object }
2525
*/
26-
export function buildAgentFlatInputSchema(
26+
export function buildAgentToolInputSchema(
2727
agentTemplate: AgentTemplate,
2828
): z.ZodType {
2929
const { inputSchema } = agentTemplate
3030

31-
// Start with an empty object schema
31+
// Build schema with prompt and params as top-level fields (consistent with spawn_agents)
32+
// Preserve the original optionality from the inputSchema
3233
let schemaFields: Record<string, z.ZodType> = {}
3334

34-
// Add prompt field if defined
3535
if (inputSchema?.prompt) {
36-
schemaFields.prompt = inputSchema.prompt.optional()
36+
schemaFields.prompt = inputSchema.prompt
3737
}
3838

39-
// Merge params fields directly into the schema (flat structure)
4039
if (inputSchema?.params) {
41-
// Try to get the shape from the params schema directly if it's a ZodObject
42-
// This preserves the full nested structure instead of converting to z.any()
43-
const paramsShape = getZodObjectShape(inputSchema.params)
44-
45-
if (paramsShape) {
46-
// We have the original Zod shape, use it directly
47-
for (const [key, fieldSchema] of Object.entries(paramsShape)) {
48-
// Skip if we already have a prompt field
49-
if (key === 'prompt') continue
50-
schemaFields[key] = fieldSchema as z.ZodType
51-
}
52-
}
40+
schemaFields.params = inputSchema.params
5341
}
5442

5543
return z
@@ -60,30 +48,6 @@ export function buildAgentFlatInputSchema(
6048
)
6149
}
6250

63-
/**
64-
* Extracts the shape from a Zod schema if it's a ZodObject.
65-
* Handles wrapped types like ZodOptional, ZodNullable, ZodDefault, etc.
66-
*/
67-
function getZodObjectShape(
68-
schema: z.ZodType,
69-
): Record<string, z.ZodType> | null {
70-
// ZodObject has a public .shape property in Zod v4
71-
if (
72-
'shape' in schema &&
73-
typeof schema.shape === 'object' &&
74-
schema.shape !== null
75-
) {
76-
return schema.shape as Record<string, z.ZodType>
77-
}
78-
79-
// Handle wrapped types (optional, nullable, default, etc.) via internal def
80-
const def = (schema as any)?._zod?.def
81-
if (def?.inner) {
82-
return getZodObjectShape(def.inner)
83-
}
84-
85-
return null
86-
}
8751

8852
/**
8953
* Builds AI SDK tool definitions for spawnable agents.
@@ -113,7 +77,7 @@ export async function buildAgentToolSet(
11377
if (!agentTemplate) continue
11478

11579
const shortName = getAgentShortName(agentType)
116-
const inputSchema = buildAgentFlatInputSchema(agentTemplate)
80+
const inputSchema = buildAgentToolInputSchema(agentTemplate)
11781

11882
// Use the same structure as other tools in toolParams
11983
toolSet[shortName] = {

packages/agent-runtime/src/tools/tool-executor.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -527,20 +527,19 @@ export function tryTransformAgentToolCall(params: {
527527
(agentType) => getAgentShortName(agentType) === toolName,
528528
)
529529

530-
// Convert to spawn_agents call
530+
// Convert to spawn_agents call - input already has prompt and params as top-level fields
531+
// (consistent with spawn_agents schema)
532+
const agentEntry: Record<string, unknown> = {
533+
agent_type: fullAgentType || toolName,
534+
}
535+
if (typeof input.prompt === 'string') {
536+
agentEntry.prompt = input.prompt
537+
}
538+
if (input.params && typeof input.params === 'object') {
539+
agentEntry.params = input.params
540+
}
531541
const spawnAgentsInput = {
532-
agents: [
533-
{
534-
agent_type: fullAgentType || toolName,
535-
...(typeof input.prompt === 'string' && { prompt: input.prompt }),
536-
// Put all other fields into params
537-
...(Object.keys(input).filter((k) => k !== 'prompt').length > 0 && {
538-
params: Object.fromEntries(
539-
Object.entries(input).filter(([k]) => k !== 'prompt'),
540-
),
541-
}),
542-
},
543-
],
542+
agents: [agentEntry],
544543
}
545544

546545
return { toolName: 'spawn_agents', input: spawnAgentsInput }

0 commit comments

Comments
 (0)