Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/release/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codebuff",
"version": "1.0.669",
"version": "1.0.671",
"description": "AI coding agent",
"license": "MIT",
"bin": {
Expand Down
6 changes: 3 additions & 3 deletions cli/src/components/tools/__tests__/apply-patch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('ApplyPatchComponent', () => {
expect(markup).toContain('src/new-file.ts')
})

test('renders update_file operation with diff content', () => {
test('renders update_file operation without diff content while diff rendering is disabled', () => {
const toolBlock = createToolBlock({
type: 'update_file',
path: 'src/existing.ts',
Expand All @@ -62,8 +62,8 @@ describe('ApplyPatchComponent', () => {
const markup = renderToStaticMarkup(result?.content as React.ReactElement)
expect(markup).toContain('Edit')
expect(markup).toContain('src/existing.ts')
expect(markup).toContain('-oldLine')
expect(markup).toContain('+newLine')
expect(markup).not.toContain('-oldLine')
expect(markup).not.toContain('+newLine')
})

test('renders delete_file operation', () => {
Expand Down
7 changes: 7 additions & 0 deletions cli/src/components/tools/diff-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ interface DiffViewerProps {
diffText: string
}

const RENDER_DIFFS = false

const DIFF_LINE_COLORS = {
dark: {
added: '#7ACC35',
Expand Down Expand Up @@ -50,6 +52,11 @@ const lineColor = (

export const DiffViewer = ({ diffText }: DiffViewerProps) => {
const theme = useTheme()

if (!RENDER_DIFFS) {
return null
}

const lines = diffText.trim().split('\n')

return (
Expand Down
4 changes: 0 additions & 4 deletions cli/src/utils/__tests__/message-block-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ describe('getAgentBaseName', () => {
expect(getAgentBaseName('file-picker')).toBe('file-picker')
})

test('normalizes direct tool aliases to canonical agent names', () => {
expect(getAgentBaseName('code_reviewer_lite')).toBe('code-reviewer-lite')
})

test('handles scoped name without version', () => {
expect(getAgentBaseName('codebuff/file-picker')).toBe('file-picker')
})
Expand Down
83 changes: 0 additions & 83 deletions cli/src/utils/__tests__/sdk-event-handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,89 +212,6 @@ describe('sdk-event-handlers', () => {
expect(getStreamingAgents().has('tool-1-0')).toBe(false)
})

test('matches underscore direct-tool aliases to hyphenated agent ids', () => {
const { ctx, getMessages, getStreamingAgents } = createTestContext()
const handleEvent = createEventHandler(ctx)
const handleChunk = createStreamChunkHandler(ctx)

handleEvent({
type: 'tool_call',
toolCallId: 'tool-1',
toolName: 'spawn_agents',
input: {
agents: [
{
agent_type: 'code_reviewer_lite',
prompt: 'Review this change',
},
],
},
agentId: 'main-agent',
parentAgentId: undefined,
} as any)

handleEvent({
type: 'subagent_start',
agentId: 'agent-real',
agentType: 'code-reviewer-lite',
displayName: 'Code Reviewer Lite',
onlyChild: true,
parentAgentId: undefined,
params: undefined,
prompt: 'Review this change',
})

handleChunk({
type: 'subagent_chunk',
agentId: 'agent-real',
agentType: 'code-reviewer-lite',
chunk: 'streamed review',
})

handleEvent({
type: 'subagent_finish',
agentId: 'agent-real',
agentType: 'code-reviewer-lite',
displayName: 'Code Reviewer Lite',
onlyChild: true,
parentAgentId: undefined,
params: undefined,
prompt: 'Review this change',
})

handleEvent({
type: 'tool_result',
toolCallId: 'tool-1',
toolName: 'spawn_agents',
output: [
{
type: 'json',
value: [
{
agentName: 'code-reviewer-lite',
agentType: 'code-reviewer-lite',
value: 'streamed review',
},
],
},
],
} as any)

const blocks = getMessages()[0].blocks ?? []
expect(blocks).toHaveLength(1)
const agentBlock = blocks[0] as AgentContentBlock
expect(agentBlock.agentId).toBe('agent-real')
expect(agentBlock.agentName).toBe('code-reviewer-lite')
expect(agentBlock.agentType).toBe('code-reviewer-lite')
expect(agentBlock.status).toBe('complete')
expect(agentBlock.blocks).toHaveLength(1)
expect(agentBlock.blocks?.[0]).toMatchObject({
type: 'text',
content: 'streamed review',
})
expect(getStreamingAgents().size).toBe(0)
})

test('handles spawn_agents tool results and clears streaming agents', () => {
const { ctx, getMessages, getStreamingAgents } = createTestContext()
ctx.message.updater.addBlock(
Expand Down
4 changes: 0 additions & 4 deletions cli/src/utils/__tests__/send-message-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1325,10 +1325,6 @@ describe('getAgentBaseName', () => {
test('returns simple name unchanged', () => {
expect(getAgentBaseName('file-picker')).toBe('file-picker')
})

test('normalizes direct tool aliases to canonical agent names', () => {
expect(getAgentBaseName('code_reviewer_lite')).toBe('code-reviewer-lite')
})
})

describe('agentTypesMatch', () => {
Expand Down
9 changes: 1 addition & 8 deletions cli/src/utils/message-block-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ import type {
* getAgentBaseName('codebuff/[email protected]') // 'file-picker'
* getAgentBaseName('[email protected]') // 'file-picker'
* getAgentBaseName('file-picker') // 'file-picker'
* getAgentBaseName('file_picker') // 'file-picker'
*/
export const getAgentBaseName = (type: string): string => {
const segment = type.split('/').pop() ?? type
return segment.split('@')[0].replace(/_/g, '-')
return segment.split('@')[0]
}

/**
Expand Down Expand Up @@ -467,7 +466,6 @@ export const moveSpawnAgentBlock = (
parentId?: string,
params?: Record<string, unknown>,
prompt?: string,
realAgentType?: string,
): ContentBlock[] => {
const updateAgentBlock = (block: ContentBlock): ContentBlock => {
if (block.type !== 'agent') {
Expand All @@ -486,11 +484,6 @@ export const moveSpawnAgentBlock = (
updatedBlock.initialPrompt = prompt
}

if (realAgentType) {
updatedBlock.agentType = realAgentType
updatedBlock.agentName = realAgentType
}

return updatedBlock
}

Expand Down
1 change: 0 additions & 1 deletion cli/src/utils/sdk-event-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ const handleSubagentStart = (
blocks,
match: spawnAgentMatch,
realAgentId: event.agentId,
realAgentType: event.agentType,
parentAgentId: event.parentAgentId,
params: event.params,
prompt: event.prompt,
Expand Down
3 changes: 0 additions & 3 deletions cli/src/utils/spawn-agent-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const resolveSpawnAgentToReal = (options: {
blocks: ContentBlock[]
match: SpawnAgentMatch
realAgentId: string
realAgentType?: string
parentAgentId?: string
params?: Record<string, unknown>
prompt?: string
Expand All @@ -37,7 +36,6 @@ export const resolveSpawnAgentToReal = (options: {
blocks,
match,
realAgentId,
realAgentType,
parentAgentId,
params: agentParams,
prompt,
Expand All @@ -50,6 +48,5 @@ export const resolveSpawnAgentToReal = (options: {
parentAgentId,
agentParams,
prompt,
realAgentType,
)
}
2 changes: 1 addition & 1 deletion freebuff/cli/release/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "freebuff",
"version": "0.0.80",
"version": "0.0.82",
"description": "The world's strongest free coding agent",
"license": "MIT",
"bin": {
Expand Down
22 changes: 0 additions & 22 deletions packages/agent-runtime/src/__tests__/run-programmatic-step.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,28 +212,6 @@ describe('runProgrammaticStep', () => {
})

describe('tool execution', () => {
it('assigns deterministic per-tool ids to handleSteps tool calls', async () => {
const mockGenerator = (function* () {
yield { toolName: 'read_files', input: { paths: ['first.txt'] } }
yield { toolName: 'read_files', input: { paths: ['second.txt'] } }
yield { toolName: 'end_turn', input: {} }
})() as StepGenerator

mockTemplate.handleSteps = () => mockGenerator

await runProgrammaticStep(mockParams)

expect(executeToolCallSpy.mock.calls[0][0].toolCallId).toBe(
'functions.read_files:0',
)
expect(executeToolCallSpy.mock.calls[1][0].toolCallId).toBe(
'functions.read_files:1',
)
expect(executeToolCallSpy.mock.calls[2][0].toolCallId).toBe(
'functions.end_turn:0',
)
})

it('should not add tool call message for add_message tool', async () => {
const mockGenerator = (function* () {
yield {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,16 +464,13 @@ describe('tool validation error handling', () => {
)
expect(toolCallEvents.length).toBe(1)
expect(toolCallEvents[0].toolName).toBe('read_files')
expect(toolCallEvents[0].toolCallId).toBe('functions.read_files:0')

// Verify tool_result event was emitted
const toolResultEvents = responseChunks.filter(
(chunk): chunk is Extract<PrintModeEvent, { type: 'tool_result' }> =>
typeof chunk !== 'string' && chunk.type === 'tool_result',
)
expect(toolResultEvents.length).toBe(1)
expect(toolResultEvents[0].toolName).toBe('read_files')
expect(toolResultEvents[0].toolCallId).toBe('functions.read_files:0')

// Verify NO error events
const errorEvents = responseChunks.filter(
Expand Down
10 changes: 3 additions & 7 deletions packages/agent-runtime/src/run-programmatic-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { cloneDeep } from 'lodash'
import { clearProposedContentForRun } from './tools/handlers/tool/proposed-content-store'
import { executeToolCall } from './tools/tool-executor'
import { parseTextWithToolCalls } from './util/parse-tool-calls-from-text'
import { createToolCallIdGenerator } from './util/tool-call-id'


import type { FileProcessingState } from './tools/handlers/tool/write-file'
import type { ExecuteToolCallParams } from './tools/tool-executor'
Expand Down Expand Up @@ -213,7 +213,6 @@ export async function runProgrammaticStep(
let toolResult: ToolResultOutput[] | undefined = undefined
let endTurn = false
let generateN: number | undefined = undefined
const getToolCallId = createToolCallIdGenerator(agentState.messageHistory)

let startTime = new Date()
let creditsBefore = agentState.directCreditsUsed
Expand Down Expand Up @@ -274,7 +273,6 @@ export async function runProgrammaticStep(
previousToolCallFinished: Promise.resolve(),
toolCalls,
toolResults,
getToolCallId,
onResponseChunk,
})
}
Expand Down Expand Up @@ -303,7 +301,6 @@ export async function runProgrammaticStep(
previousToolCallFinished: Promise.resolve(),
toolCalls,
toolResults,
getToolCallId,
onResponseChunk,
})

Expand Down Expand Up @@ -435,7 +432,6 @@ type ExecuteToolCallsArrayParams = Omit<
| 'toolResultsToAddToMessageHistory'
> & {
agentState: AgentState
getToolCallId: (toolName: string) => string
onResponseChunk: (chunk: string | PrintModeEvent) => void
}

Expand All @@ -449,7 +445,7 @@ async function executeSingleToolCall(
toolCallToExecute: ToolCallToExecute,
params: ExecuteToolCallsArrayParams,
): Promise<ToolResultOutput[] | undefined> {
const { agentState, getToolCallId, onResponseChunk, toolResults } = params
const { agentState, onResponseChunk, toolResults } = params

// Note: We don't check if the tool is available for the agent template anymore.
// You can run any tool from handleSteps now!
Expand All @@ -459,7 +455,7 @@ async function executeSingleToolCall(
// )
// }

const toolCallId = getToolCallId(toolCallToExecute.toolName)
const toolCallId = crypto.randomUUID()
const excludeToolFromMessageHistory =
toolCallToExecute.includeToolCall === false

Expand Down
4 changes: 4 additions & 0 deletions packages/agent-runtime/src/tool-stream-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export async function* processStreamWithTools(params: {
}
trackEvent: TrackEventFn
executeXmlToolCall: (params: {
toolCallId: string
toolName: string
input: Record<string, unknown>
}) => Promise<void>
Expand Down Expand Up @@ -149,9 +150,12 @@ export async function* processStreamWithTools(params: {

// Then process and yield any XML tool calls found
for (const toolCall of toolCalls) {
const toolCallId = `xml-${crypto.randomUUID().slice(0, 8)}`

// Execute the tool immediately if callback provided, pausing the stream
// The callback handles emitting tool_call and tool_result events
await executeXmlToolCall({
toolCallId,
toolName: toolCall.toolName,
input: toolCall.input,
})
Expand Down
Loading
Loading