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
369 changes: 223 additions & 146 deletions .agents/__tests__/context-pruner.test.ts

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .agents/base2/base2-factory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { publisher } from '../constants'

import {
PLACEHOLDER,
type SecretAgentDefinition,
} from '../types/secret-agent-definition'

import type { ModelName } from 'types/agent-definition'

export const base2 = (model: ModelName): Omit<SecretAgentDefinition, 'id'> => ({
Expand Down Expand Up @@ -76,6 +76,7 @@ ${PLACEHOLDER.GIT_CHANGES_PROMPT}
agent_type: 'context-pruner',
params: params ?? {},
},
includeToolCall: false,
} as any

const { stepsComplete } = yield 'STEP'
Expand All @@ -87,6 +88,7 @@ ${PLACEHOLDER.GIT_CHANGES_PROMPT}
role: 'user',
content: `You have reached the step limit. Please summarize your progress in plain text (no need to use set_output) so far and what you still need to solve. Immediately after summarizing, please end your turn. Do not use any tools except for the end_turn tool.`,
},
includeToolCall: false,
}
yield 'STEP'
break
Expand Down
2 changes: 1 addition & 1 deletion .agents/base2/editor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { publisher } from '../constants'

import {
PLACEHOLDER,
type SecretAgentDefinition,
Expand Down Expand Up @@ -150,6 +149,7 @@ ${PLACEHOLDER.KNOWLEDGE_FILES_CONTENTS}`,
content:
'You have reached the step limit. Please use the set_output tool now to summarize your progress so far, what you still need to solve, and provide any insights that could help complete the remaining work. Please end your turn after using the set_output tool with the end_turn tool.',
},
includeToolCall: false,
}

// One final step to produce the summary
Expand Down
25 changes: 19 additions & 6 deletions .agents/base2/planner-factory.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ModelName, ToolCall } from 'types/agent-definition'
import { publisher } from '../constants'
import {
PLACEHOLDER,
type SecretAgentDefinition,
} from '../types/secret-agent-definition'

import type { ModelName, ToolCall } from 'types/agent-definition'

export const plannerFactory = (
model: ModelName,
): Omit<SecretAgentDefinition, 'id'> => ({
Expand Down Expand Up @@ -51,11 +52,23 @@ ${PLACEHOLDER.KNOWLEDGE_FILES_CONTENTS}`,
agentState.messageHistory
.slice(2)
.map((message) =>
typeof message.content === 'string'
? message.content
: message.content
.map((content) => (content.type === 'text' ? content.text : ''))
.join('\n'),
message.role === 'tool'
? JSON.stringify(
{
toolName: message.content.toolName,
toolCallId: message.content.toolCallId,
output: message.content.output,
},
null,
2,
)
: typeof message.content === 'string'
? message.content
: message.content
.map((content) =>
content.type === 'text' ? content.text : '',
)
.join('\n'),
)
.join('\n')

Expand Down
1 change: 1 addition & 0 deletions .agents/changes-reviewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Use the following guidelines to review the changes and suggest improvements:
content:
'Now I will spawn a file explorer to find any missing codebase context, and then review the changes.',
},
includeToolCall: false,
}

yield 'STEP_ALL'
Expand Down
126 changes: 65 additions & 61 deletions .agents/context-pruner.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { publisher } from './constants'

import type {
AgentDefinition,
Message,
ToolCall,
} from './types/agent-definition'
import type { AgentDefinition, ToolCall } from './types/agent-definition'
import type { Message, ToolMessage } from './types/codebuff-message'
import type { CodebuffToolMessage } from '@codebuff/common/tools/list'

const definition: AgentDefinition = {
id: 'context-pruner',
Expand Down Expand Up @@ -43,31 +41,13 @@ const definition: AgentDefinition = {

let currentMessages = [...messages]

// Find and remove context-pruner spawn_agent_inline call and following messages
const lastAssistantMessageIndex = currentMessages.findLastIndex(
(message) => message.role === 'assistant',
)
const lastAssistantMessage = currentMessages[lastAssistantMessageIndex]
const lastAssistantMessageIsToolCall =
typeof lastAssistantMessage?.content === 'string' &&
lastAssistantMessage.content.includes('spawn_agent_inline') &&
lastAssistantMessage.content.includes('context-pruner')

if (lastAssistantMessageIsToolCall && lastAssistantMessageIndex >= 0) {
// Remove tool call and any following messages.
const messagesToRemove =
currentMessages.length - lastAssistantMessageIndex
currentMessages.splice(lastAssistantMessageIndex, messagesToRemove)
}

// Initial check - if already under limit, return (with inline agent tool call removed)
// Initial check - if already under limit, return
const initialTokens = countTokensJson(currentMessages)
if (initialTokens < maxMessageTokens) {
yield {
toolName: 'set_messages',
input: {
messages: currentMessages,
},
input: { messages: currentMessages },
includeToolCall: false,
}
return
}
Expand All @@ -78,25 +58,41 @@ const definition: AgentDefinition = {

for (let i = currentMessages.length - 1; i >= 0; i--) {
const message = currentMessages[i]
let processedContent =
typeof message.content === 'string'
? message.content
: JSON.stringify(message.content)

if (processedContent.includes('<tool>run_terminal_command</tool>')) {
// Handle tool messages with new object format
if (
message.role === 'tool' &&
message.content.toolName === 'run_terminal_command'
) {
const toolMessage =
message as CodebuffToolMessage<'run_terminal_command'>

if (numKeptTerminalCommands < numTerminalCommandsToKeep) {
numKeptTerminalCommands++
afterTerminalPass.unshift({ ...message, content: processedContent })
afterTerminalPass.unshift(message)
} else {
// Simplify terminal command result
processedContent = processedContent.replace(
/<tool_result>\s*<tool>run_terminal_command<\/tool>\s*<result>[\s\S]*?<\/result>\s*<\/tool_result>/g,
'<tool_result><tool>run_terminal_command</tool><result>[Output omitted]</result></tool_result>',
)
afterTerminalPass.unshift({ ...message, content: processedContent })
// Simplify terminal command result by replacing output
const simplifiedMessage: CodebuffToolMessage<'run_terminal_command'> =
{
...toolMessage,
content: {
...toolMessage.content,
output: [
{
type: 'json',
value: {
command:
toolMessage.content.output[0]?.value?.command || '',
stdoutOmittedForLength: true,
},
},
],
},
}
afterTerminalPass.unshift(simplifiedMessage)
}
} else {
afterTerminalPass.unshift({ ...message, content: processedContent })
afterTerminalPass.unshift(message)
}
}

Expand All @@ -108,28 +104,37 @@ const definition: AgentDefinition = {
input: {
messages: afterTerminalPass,
},
includeToolCall: false,
}
return
}

// PASS 2: Remove large tool results (any tool result > 1000 chars)
// PASS 2: Remove large tool results (any tool result output > 1000 chars when stringified)
const afterToolResultsPass = afterTerminalPass.map((message) => {
let processedContent =
typeof message.content === 'string'
? message.content
: JSON.stringify(message.content)

if (
processedContent.includes('<tool_result>') &&
processedContent.length > 1000
) {
processedContent = processedContent.replace(
/<result>[\s\S]*?<\/result>/g,
'<result>[Large tool result omitted]</result>',
)
if (message.role === 'tool') {
const outputSize = JSON.stringify(message.content.output).length

if (outputSize > 1000) {
// Replace with simplified output
const simplifiedMessage: ToolMessage = {
...message,
content: {
...message.content,
output: [
{
type: 'json',
value: {
message: '[LARGE_TOOL_RESULT_OMITTED]',
originalSize: outputSize,
},
},
],
},
}
return simplifiedMessage
}
}

return { ...message, content: processedContent }
return message
})

// Check if tool results pass was enough
Expand All @@ -140,7 +145,8 @@ const definition: AgentDefinition = {
input: {
messages: afterToolResultsPass,
},
} satisfies ToolCall
includeToolCall: false,
} satisfies ToolCall<'set_messages'>
return
}

Expand All @@ -162,10 +168,7 @@ const definition: AgentDefinition = {
const filteredMessages: any[] = []

for (const message of afterToolResultsPass) {
if (
removedTokens >= tokensToRemove ||
(message as any).keepDuringTruncation
) {
if (removedTokens >= tokensToRemove || message.keepDuringTruncation) {
filteredMessages.push(message)
continue
}
Expand All @@ -190,7 +193,8 @@ const definition: AgentDefinition = {
input: {
messages: finalMessages,
},
} satisfies ToolCall
includeToolCall: false,
} satisfies ToolCall<'set_messages'>
},
}

Expand Down
1 change: 1 addition & 0 deletions .agents/factory/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const base = (model: ModelName): Omit<SecretAgentDefinition, 'id'> => ({
agent_type: 'context-pruner',
params: params ?? {},
},
includeToolCall: false,
} as any

const { stepsComplete } = yield 'STEP'
Expand Down
2 changes: 2 additions & 0 deletions .agents/git-committer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const definition: AgentDefinition = {
content:
"I've analyzed the git diff and recent commit history. Now I'll read any relevant files to better understand the context of these changes.",
},
includeToolCall: false,
}

// Step 3: Let AI generate a step to decide which files to read.
Expand All @@ -73,6 +74,7 @@ const definition: AgentDefinition = {
content:
"Now I'll analyze the changes and create a commit with a good commit message.",
},
includeToolCall: false,
}

yield 'STEP_ALL'
Expand Down
26 changes: 5 additions & 21 deletions .agents/types/agent-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
* export default definition
*/

import type { Message } from './codebuff-message'
import type * as Tools from './tools'
type ToolName = Tools.ToolName

// ============================================================================
// Agent Definition and Utility Types
// ============================================================================
Expand Down Expand Up @@ -201,25 +205,6 @@ export interface AgentState {
output: Record<string, any> | undefined
}

/**
* Message in conversation history
*/
export interface Message {
role: 'user' | 'assistant'
content:
| string
| Array<
| {
type: 'text'
text: string
}
| {
type: 'image'
image: string
}
>
}

/**
* Context provided to handleSteps generator function
*/
Expand All @@ -236,6 +221,7 @@ export type ToolCall<T extends ToolName = ToolName> = {
[K in T]: {
toolName: K
input: Tools.GetToolParams<K>
includeToolCall?: boolean
}
}[T]

Expand Down Expand Up @@ -362,6 +348,4 @@ export type ModelName =
| 'z-ai/glm-4.5:nitro'
| (string & {})

import type * as Tools from './tools'
export type { Tools }
type ToolName = Tools.ToolName
Loading