Skip to content

Commit 2fea71c

Browse files
authored
Object tool results (#252)
1 parent 0571809 commit 2fea71c

File tree

137 files changed

+3486
-2793
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+3486
-2793
lines changed

.agents/__tests__/context-pruner.test.ts

Lines changed: 223 additions & 146 deletions
Large diffs are not rendered by default.

.agents/base2/base2-factory.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { publisher } from '../constants'
2-
32
import {
43
PLACEHOLDER,
54
type SecretAgentDefinition,
65
} from '../types/secret-agent-definition'
6+
77
import type { ModelName } from 'types/agent-definition'
88

99
export const base2 = (model: ModelName): Omit<SecretAgentDefinition, 'id'> => ({
@@ -76,6 +76,7 @@ ${PLACEHOLDER.GIT_CHANGES_PROMPT}
7676
agent_type: 'context-pruner',
7777
params: params ?? {},
7878
},
79+
includeToolCall: false,
7980
} as any
8081

8182
const { stepsComplete } = yield 'STEP'
@@ -87,6 +88,7 @@ ${PLACEHOLDER.GIT_CHANGES_PROMPT}
8788
role: 'user',
8889
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.`,
8990
},
91+
includeToolCall: false,
9092
}
9193
yield 'STEP'
9294
break

.agents/base2/editor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { publisher } from '../constants'
2-
32
import {
43
PLACEHOLDER,
54
type SecretAgentDefinition,
@@ -150,6 +149,7 @@ ${PLACEHOLDER.KNOWLEDGE_FILES_CONTENTS}`,
150149
content:
151150
'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.',
152151
},
152+
includeToolCall: false,
153153
}
154154

155155
// One final step to produce the summary

.agents/base2/planner-factory.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { ModelName, ToolCall } from 'types/agent-definition'
21
import { publisher } from '../constants'
32
import {
43
PLACEHOLDER,
54
type SecretAgentDefinition,
65
} from '../types/secret-agent-definition'
76

7+
import type { ModelName, ToolCall } from 'types/agent-definition'
8+
89
export const plannerFactory = (
910
model: ModelName,
1011
): Omit<SecretAgentDefinition, 'id'> => ({
@@ -51,11 +52,23 @@ ${PLACEHOLDER.KNOWLEDGE_FILES_CONTENTS}`,
5152
agentState.messageHistory
5253
.slice(2)
5354
.map((message) =>
54-
typeof message.content === 'string'
55-
? message.content
56-
: message.content
57-
.map((content) => (content.type === 'text' ? content.text : ''))
58-
.join('\n'),
55+
message.role === 'tool'
56+
? JSON.stringify(
57+
{
58+
toolName: message.content.toolName,
59+
toolCallId: message.content.toolCallId,
60+
output: message.content.output,
61+
},
62+
null,
63+
2,
64+
)
65+
: typeof message.content === 'string'
66+
? message.content
67+
: message.content
68+
.map((content) =>
69+
content.type === 'text' ? content.text : '',
70+
)
71+
.join('\n'),
5972
)
6073
.join('\n')
6174

.agents/changes-reviewer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Use the following guidelines to review the changes and suggest improvements:
110110
content:
111111
'Now I will spawn a file explorer to find any missing codebase context, and then review the changes.',
112112
},
113+
includeToolCall: false,
113114
}
114115

115116
yield 'STEP_ALL'

.agents/context-pruner.ts

Lines changed: 65 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { publisher } from './constants'
22

3-
import type {
4-
AgentDefinition,
5-
Message,
6-
ToolCall,
7-
} from './types/agent-definition'
3+
import type { AgentDefinition, ToolCall } from './types/agent-definition'
4+
import type { Message, ToolMessage } from './types/codebuff-message'
5+
import type { CodebuffToolMessage } from '@codebuff/common/tools/list'
86

97
const definition: AgentDefinition = {
108
id: 'context-pruner',
@@ -43,31 +41,13 @@ const definition: AgentDefinition = {
4341

4442
let currentMessages = [...messages]
4543

46-
// Find and remove context-pruner spawn_agent_inline call and following messages
47-
const lastAssistantMessageIndex = currentMessages.findLastIndex(
48-
(message) => message.role === 'assistant',
49-
)
50-
const lastAssistantMessage = currentMessages[lastAssistantMessageIndex]
51-
const lastAssistantMessageIsToolCall =
52-
typeof lastAssistantMessage?.content === 'string' &&
53-
lastAssistantMessage.content.includes('spawn_agent_inline') &&
54-
lastAssistantMessage.content.includes('context-pruner')
55-
56-
if (lastAssistantMessageIsToolCall && lastAssistantMessageIndex >= 0) {
57-
// Remove tool call and any following messages.
58-
const messagesToRemove =
59-
currentMessages.length - lastAssistantMessageIndex
60-
currentMessages.splice(lastAssistantMessageIndex, messagesToRemove)
61-
}
62-
63-
// Initial check - if already under limit, return (with inline agent tool call removed)
44+
// Initial check - if already under limit, return
6445
const initialTokens = countTokensJson(currentMessages)
6546
if (initialTokens < maxMessageTokens) {
6647
yield {
6748
toolName: 'set_messages',
68-
input: {
69-
messages: currentMessages,
70-
},
49+
input: { messages: currentMessages },
50+
includeToolCall: false,
7151
}
7252
return
7353
}
@@ -78,25 +58,41 @@ const definition: AgentDefinition = {
7858

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

86-
if (processedContent.includes('<tool>run_terminal_command</tool>')) {
62+
// Handle tool messages with new object format
63+
if (
64+
message.role === 'tool' &&
65+
message.content.toolName === 'run_terminal_command'
66+
) {
67+
const toolMessage =
68+
message as CodebuffToolMessage<'run_terminal_command'>
69+
8770
if (numKeptTerminalCommands < numTerminalCommandsToKeep) {
8871
numKeptTerminalCommands++
89-
afterTerminalPass.unshift({ ...message, content: processedContent })
72+
afterTerminalPass.unshift(message)
9073
} else {
91-
// Simplify terminal command result
92-
processedContent = processedContent.replace(
93-
/<tool_result>\s*<tool>run_terminal_command<\/tool>\s*<result>[\s\S]*?<\/result>\s*<\/tool_result>/g,
94-
'<tool_result><tool>run_terminal_command</tool><result>[Output omitted]</result></tool_result>',
95-
)
96-
afterTerminalPass.unshift({ ...message, content: processedContent })
74+
// Simplify terminal command result by replacing output
75+
const simplifiedMessage: CodebuffToolMessage<'run_terminal_command'> =
76+
{
77+
...toolMessage,
78+
content: {
79+
...toolMessage.content,
80+
output: [
81+
{
82+
type: 'json',
83+
value: {
84+
command:
85+
toolMessage.content.output[0]?.value?.command || '',
86+
stdoutOmittedForLength: true,
87+
},
88+
},
89+
],
90+
},
91+
}
92+
afterTerminalPass.unshift(simplifiedMessage)
9793
}
9894
} else {
99-
afterTerminalPass.unshift({ ...message, content: processedContent })
95+
afterTerminalPass.unshift(message)
10096
}
10197
}
10298

@@ -108,28 +104,37 @@ const definition: AgentDefinition = {
108104
input: {
109105
messages: afterTerminalPass,
110106
},
107+
includeToolCall: false,
111108
}
112109
return
113110
}
114111

115-
// PASS 2: Remove large tool results (any tool result > 1000 chars)
112+
// PASS 2: Remove large tool results (any tool result output > 1000 chars when stringified)
116113
const afterToolResultsPass = afterTerminalPass.map((message) => {
117-
let processedContent =
118-
typeof message.content === 'string'
119-
? message.content
120-
: JSON.stringify(message.content)
121-
122-
if (
123-
processedContent.includes('<tool_result>') &&
124-
processedContent.length > 1000
125-
) {
126-
processedContent = processedContent.replace(
127-
/<result>[\s\S]*?<\/result>/g,
128-
'<result>[Large tool result omitted]</result>',
129-
)
114+
if (message.role === 'tool') {
115+
const outputSize = JSON.stringify(message.content.output).length
116+
117+
if (outputSize > 1000) {
118+
// Replace with simplified output
119+
const simplifiedMessage: ToolMessage = {
120+
...message,
121+
content: {
122+
...message.content,
123+
output: [
124+
{
125+
type: 'json',
126+
value: {
127+
message: '[LARGE_TOOL_RESULT_OMITTED]',
128+
originalSize: outputSize,
129+
},
130+
},
131+
],
132+
},
133+
}
134+
return simplifiedMessage
135+
}
130136
}
131-
132-
return { ...message, content: processedContent }
137+
return message
133138
})
134139

135140
// Check if tool results pass was enough
@@ -140,7 +145,8 @@ const definition: AgentDefinition = {
140145
input: {
141146
messages: afterToolResultsPass,
142147
},
143-
} satisfies ToolCall
148+
includeToolCall: false,
149+
} satisfies ToolCall<'set_messages'>
144150
return
145151
}
146152

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

164170
for (const message of afterToolResultsPass) {
165-
if (
166-
removedTokens >= tokensToRemove ||
167-
(message as any).keepDuringTruncation
168-
) {
171+
if (removedTokens >= tokensToRemove || message.keepDuringTruncation) {
169172
filteredMessages.push(message)
170173
continue
171174
}
@@ -190,7 +193,8 @@ const definition: AgentDefinition = {
190193
input: {
191194
messages: finalMessages,
192195
},
193-
} satisfies ToolCall
196+
includeToolCall: false,
197+
} satisfies ToolCall<'set_messages'>
194198
},
195199
}
196200

.agents/factory/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const base = (model: ModelName): Omit<SecretAgentDefinition, 'id'> => ({
6868
agent_type: 'context-pruner',
6969
params: params ?? {},
7070
},
71+
includeToolCall: false,
7172
} as any
7273

7374
const { stepsComplete } = yield 'STEP'

.agents/git-committer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const definition: AgentDefinition = {
6060
content:
6161
"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.",
6262
},
63+
includeToolCall: false,
6364
}
6465

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

7880
yield 'STEP_ALL'

.agents/types/agent-definition.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
* export default definition
1515
*/
1616

17+
import type { Message } from './codebuff-message'
18+
import type * as Tools from './tools'
19+
type ToolName = Tools.ToolName
20+
1721
// ============================================================================
1822
// Agent Definition and Utility Types
1923
// ============================================================================
@@ -201,25 +205,6 @@ export interface AgentState {
201205
output: Record<string, any> | undefined
202206
}
203207

204-
/**
205-
* Message in conversation history
206-
*/
207-
export interface Message {
208-
role: 'user' | 'assistant'
209-
content:
210-
| string
211-
| Array<
212-
| {
213-
type: 'text'
214-
text: string
215-
}
216-
| {
217-
type: 'image'
218-
image: string
219-
}
220-
>
221-
}
222-
223208
/**
224209
* Context provided to handleSteps generator function
225210
*/
@@ -236,6 +221,7 @@ export type ToolCall<T extends ToolName = ToolName> = {
236221
[K in T]: {
237222
toolName: K
238223
input: Tools.GetToolParams<K>
224+
includeToolCall?: boolean
239225
}
240226
}[T]
241227

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

365-
import type * as Tools from './tools'
366351
export type { Tools }
367-
type ToolName = Tools.ToolName

0 commit comments

Comments
 (0)