Skip to content

Commit 011af3a

Browse files
committed
Fix duplicate reviewer agent card
1 parent 5fd1dbd commit 011af3a

7 files changed

Lines changed: 115 additions & 4 deletions

File tree

cli/src/utils/__tests__/message-block-helpers.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ describe('getAgentBaseName', () => {
3939
expect(getAgentBaseName('file-picker')).toBe('file-picker')
4040
})
4141

42+
test('normalizes direct tool aliases to canonical agent names', () => {
43+
expect(getAgentBaseName('code_reviewer_lite')).toBe('code-reviewer-lite')
44+
})
45+
4246
test('handles scoped name without version', () => {
4347
expect(getAgentBaseName('codebuff/file-picker')).toBe('file-picker')
4448
})

cli/src/utils/__tests__/sdk-event-handlers.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,89 @@ describe('sdk-event-handlers', () => {
212212
expect(getStreamingAgents().has('tool-1-0')).toBe(false)
213213
})
214214

215+
test('matches underscore direct-tool aliases to hyphenated agent ids', () => {
216+
const { ctx, getMessages, getStreamingAgents } = createTestContext()
217+
const handleEvent = createEventHandler(ctx)
218+
const handleChunk = createStreamChunkHandler(ctx)
219+
220+
handleEvent({
221+
type: 'tool_call',
222+
toolCallId: 'tool-1',
223+
toolName: 'spawn_agents',
224+
input: {
225+
agents: [
226+
{
227+
agent_type: 'code_reviewer_lite',
228+
prompt: 'Review this change',
229+
},
230+
],
231+
},
232+
agentId: 'main-agent',
233+
parentAgentId: undefined,
234+
} as any)
235+
236+
handleEvent({
237+
type: 'subagent_start',
238+
agentId: 'agent-real',
239+
agentType: 'code-reviewer-lite',
240+
displayName: 'Code Reviewer Lite',
241+
onlyChild: true,
242+
parentAgentId: undefined,
243+
params: undefined,
244+
prompt: 'Review this change',
245+
})
246+
247+
handleChunk({
248+
type: 'subagent_chunk',
249+
agentId: 'agent-real',
250+
agentType: 'code-reviewer-lite',
251+
chunk: 'streamed review',
252+
})
253+
254+
handleEvent({
255+
type: 'subagent_finish',
256+
agentId: 'agent-real',
257+
agentType: 'code-reviewer-lite',
258+
displayName: 'Code Reviewer Lite',
259+
onlyChild: true,
260+
parentAgentId: undefined,
261+
params: undefined,
262+
prompt: 'Review this change',
263+
})
264+
265+
handleEvent({
266+
type: 'tool_result',
267+
toolCallId: 'tool-1',
268+
toolName: 'spawn_agents',
269+
output: [
270+
{
271+
type: 'json',
272+
value: [
273+
{
274+
agentName: 'code-reviewer-lite',
275+
agentType: 'code-reviewer-lite',
276+
value: 'streamed review',
277+
},
278+
],
279+
},
280+
],
281+
} as any)
282+
283+
const blocks = getMessages()[0].blocks ?? []
284+
expect(blocks).toHaveLength(1)
285+
const agentBlock = blocks[0] as AgentContentBlock
286+
expect(agentBlock.agentId).toBe('agent-real')
287+
expect(agentBlock.agentName).toBe('code-reviewer-lite')
288+
expect(agentBlock.agentType).toBe('code-reviewer-lite')
289+
expect(agentBlock.status).toBe('complete')
290+
expect(agentBlock.blocks).toHaveLength(1)
291+
expect(agentBlock.blocks?.[0]).toMatchObject({
292+
type: 'text',
293+
content: 'streamed review',
294+
})
295+
expect(getStreamingAgents().size).toBe(0)
296+
})
297+
215298
test('handles spawn_agents tool results and clears streaming agents', () => {
216299
const { ctx, getMessages, getStreamingAgents } = createTestContext()
217300
ctx.message.updater.addBlock(

cli/src/utils/__tests__/send-message-helpers.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,10 @@ describe('getAgentBaseName', () => {
13251325
test('returns simple name unchanged', () => {
13261326
expect(getAgentBaseName('file-picker')).toBe('file-picker')
13271327
})
1328+
1329+
test('normalizes direct tool aliases to canonical agent names', () => {
1330+
expect(getAgentBaseName('code_reviewer_lite')).toBe('code-reviewer-lite')
1331+
})
13281332
})
13291333

13301334
describe('agentTypesMatch', () => {

cli/src/utils/message-block-helpers.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import type {
1616
* getAgentBaseName('codebuff/file-picker@0.0.2') // 'file-picker'
1717
* getAgentBaseName('file-picker@1.0.0') // 'file-picker'
1818
* getAgentBaseName('file-picker') // 'file-picker'
19+
* getAgentBaseName('file_picker') // 'file-picker'
1920
*/
2021
export const getAgentBaseName = (type: string): string => {
2122
const segment = type.split('/').pop() ?? type
22-
return segment.split('@')[0]
23+
return segment.split('@')[0].replace(/_/g, '-')
2324
}
2425

2526
/**
@@ -466,6 +467,7 @@ export const moveSpawnAgentBlock = (
466467
parentId?: string,
467468
params?: Record<string, unknown>,
468469
prompt?: string,
470+
realAgentType?: string,
469471
): ContentBlock[] => {
470472
const updateAgentBlock = (block: ContentBlock): ContentBlock => {
471473
if (block.type !== 'agent') {
@@ -484,6 +486,11 @@ export const moveSpawnAgentBlock = (
484486
updatedBlock.initialPrompt = prompt
485487
}
486488

489+
if (realAgentType) {
490+
updatedBlock.agentType = realAgentType
491+
updatedBlock.agentName = realAgentType
492+
}
493+
487494
return updatedBlock
488495
}
489496

cli/src/utils/sdk-event-handlers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ const handleSubagentStart = (
183183
blocks,
184184
match: spawnAgentMatch,
185185
realAgentId: event.agentId,
186+
realAgentType: event.agentType,
186187
parentAgentId: event.parentAgentId,
187188
params: event.params,
188189
prompt: event.prompt,

cli/src/utils/spawn-agent-matcher.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const resolveSpawnAgentToReal = (options: {
2828
blocks: ContentBlock[]
2929
match: SpawnAgentMatch
3030
realAgentId: string
31+
realAgentType?: string
3132
parentAgentId?: string
3233
params?: Record<string, unknown>
3334
prompt?: string
@@ -36,6 +37,7 @@ export const resolveSpawnAgentToReal = (options: {
3637
blocks,
3738
match,
3839
realAgentId,
40+
realAgentType,
3941
parentAgentId,
4042
params: agentParams,
4143
prompt,
@@ -48,5 +50,6 @@ export const resolveSpawnAgentToReal = (options: {
4850
parentAgentId,
4951
agentParams,
5052
prompt,
53+
realAgentType,
5154
)
5255
}

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { endsAgentStepParam, toolNames } from '@codebuff/common/tools/constants'
22
import { toolParams } from '@codebuff/common/tools/list'
3+
import { normalizeAgentIdForLookup } from '@codebuff/common/util/agent-id-parsing'
34
import { cloneDeep } from 'lodash'
45

56
import { getMCPToolData } from '../mcp'
@@ -371,7 +372,9 @@ export async function executeToolCall<T extends ToolName>(
371372
}
372373
}
373374

374-
let agentIdToLoad = agentTypeStr
375+
let agentIdToLoad = isBaseAgent
376+
? normalizeAgentIdForLookup(agentTypeStr)
377+
: agentTypeStr
375378
if (!isBaseAgent) {
376379
const matchingSpawn = getMatchingSpawn(
377380
agentTemplate.spawnableAgents,
@@ -420,7 +423,13 @@ export async function executeToolCall<T extends ToolName>(
420423
}
421424
}
422425

423-
return { valid: true as const, agent }
426+
return {
427+
valid: true as const,
428+
agent: {
429+
...(agent as Record<string, unknown>),
430+
agent_type: agentIdToLoad,
431+
},
432+
}
424433
}),
425434
)
426435

@@ -449,8 +458,8 @@ export async function executeToolCall<T extends ToolName>(
449458
}
450459
const errorMsg = `Some agents could not be spawned: ${errors.join('; ')}. Proceeding with valid agents only.`
451460
onResponseChunk({ type: 'error', message: errorMsg })
452-
effectiveInput = { ...effectiveInput, agents: validAgents }
453461
}
462+
effectiveInput = { ...effectiveInput, agents: validAgents }
454463
}
455464
}
456465

0 commit comments

Comments
 (0)