Skip to content

Commit 64d884c

Browse files
committed
Merge origin/main into image-support
Resolved conflict in use-send-message.ts by combining: - Image support: MessageContent type, effectivePrompt for image-only messages, content param - Bash context: prepending bash history to prompts for LLM context
2 parents 193c46a + d27fe0c commit 64d884c

File tree

6 files changed

+225
-149
lines changed

6 files changed

+225
-149
lines changed

.agents/editor/editor.ts

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@ import { publisher } from '../constants'
33

44
export const createCodeEditor = (options: {
55
model: 'gpt-5' | 'opus'
6-
}): Omit<AgentDefinition, 'id'> => ({
7-
publisher,
8-
model:
9-
options.model === 'gpt-5' ? 'openai/gpt-5.1' : 'anthropic/claude-opus-4.5',
10-
displayName: 'Code Editor',
11-
spawnerPrompt:
12-
"Expert code editor that implements code changes based on the user's request. Do not specify an input prompt for this agent; it inherits the context of the entire conversation with the user. Make sure to read any files intended to be edited before spawning this agent as it cannot read files on its own.",
13-
outputMode: 'structured_output',
14-
toolNames: ['write_file', 'str_replace', 'set_output'],
15-
16-
includeMessageHistory: true,
17-
inheritParentSystemPrompt: true,
18-
19-
instructionsPrompt: `You are an expert code editor with deep understanding of software engineering principles. You were spawned to generate an implementation for the user's request.
6+
}): Omit<AgentDefinition, 'id'> => {
7+
const { model } = options
8+
return {
9+
publisher,
10+
model:
11+
options.model === 'gpt-5'
12+
? 'openai/gpt-5.1'
13+
: 'anthropic/claude-opus-4.5',
14+
displayName: 'Code Editor',
15+
spawnerPrompt:
16+
"Expert code editor that implements code changes based on the user's request. Do not specify an input prompt for this agent; it inherits the context of the entire conversation with the user. Make sure to read any files intended to be edited before spawning this agent as it cannot read files on its own.",
17+
outputMode: 'structured_output',
18+
toolNames: ['write_file', 'str_replace', 'set_output'],
19+
20+
includeMessageHistory: true,
21+
inheritParentSystemPrompt: true,
22+
23+
instructionsPrompt: `You are an expert code editor with deep understanding of software engineering principles. You were spawned to generate an implementation for the user's request.
2024
2125
Your task is to write out ALL the code changes needed to complete the user's request in a single comprehensive response.
2226
23-
Important: You can not make any other tool calls besides editing files. You cannot read more files, write todos, spawn agents, or set output. Do not call any of these tools!
27+
Important: You can not make any other tool calls besides editing files. You cannot read more files, write todos, spawn agents, or set output. set_output in particular should not be used. Do not call any of these tools!
2428
2529
Write out what changes you would make using the tool call format below. Use this exact format for each file change:
2630
@@ -52,7 +56,10 @@ OR for new files or major rewrites:
5256
}
5357
</codebuff_tool_call>
5458
55-
IMPORTANT: Before you start writing your implementation, you should use <think> tags to think about the best way to implement the changes. You should think really really hard to make sure you implement the changes in the best way possible. Take as much time as you to think through all the cases to produce the best changes.
59+
${
60+
model === 'gpt-5'
61+
? ''
62+
: `IMPORTANT: Before you start writing your implementation, you should use <think> tags to think about the best way to implement the changes. You should think really really hard to make sure you implement the changes in the best way possible. Take as much time as you to think through all the cases to produce the best changes.
5663
5764
You can also use <think> tags interspersed between tool calls to think about the best way to implement the changes.
5865
@@ -78,9 +85,8 @@ You can also use <think> tags interspersed between tool calls to think about the
7885
[ Third tool call to implement the feature ]
7986
</codebuff_tool_call>
8087
81-
</example>
82-
83-
After the edit tool calls, you can optionally mention any follow-up steps to take, like deleting a file, or a sepcific way to validate the changes. You should not summarize your changes, just stop when you're done. There's no need to use the set_output tool as your entire response will be included in the output.
88+
</example>`
89+
}
8490
8591
Your implementation should:
8692
- Be complete and comprehensive
@@ -97,46 +103,64 @@ More style notes:
97103
98104
Write out your complete implementation now, formatting all changes as tool calls as shown above.`,
99105

100-
handleSteps: function* ({ agentState: initialAgentState }) {
101-
const initialMessageHistoryLength = initialAgentState.messageHistory.length
102-
const { agentState } = yield 'STEP'
103-
const { messageHistory } = agentState
104-
105-
const newMessages = messageHistory.slice(initialMessageHistoryLength)
106-
const assistantText = newMessages
107-
.filter((message) => message.role === 'assistant')
108-
.flatMap((message) => message.content)
109-
.filter((content) => content.type === 'text')
110-
.map((content) => content.text)
111-
.join('\n')
112-
113-
const { agentState: postAssistantTextAgentState } = yield {
114-
type: 'STEP_TEXT',
115-
text: assistantText,
116-
} as StepText
117-
118-
const postAssistantTextMessageHistory =
119-
postAssistantTextAgentState.messageHistory.slice(
120-
initialMessageHistoryLength,
121-
)
122-
const toolResults = postAssistantTextMessageHistory
123-
.filter((message) => message.role === 'tool')
124-
.flatMap((message) => message.content)
125-
.filter((content) => content.type === 'json')
126-
.map((content) => content.value)
127-
128-
yield {
129-
toolName: 'set_output',
130-
input: {
131-
output: {
132-
message: assistantText,
133-
toolResults,
106+
handleSteps: function* ({ agentState: initialAgentState }) {
107+
const initialMessageHistoryLength =
108+
initialAgentState.messageHistory.length
109+
const { agentState } = yield 'STEP'
110+
const { messageHistory } = agentState
111+
112+
const newMessages = messageHistory.slice(initialMessageHistoryLength)
113+
const assistantText = newMessages
114+
.filter((message) => message.role === 'assistant')
115+
.flatMap((message) => message.content)
116+
.filter((content) => content.type === 'text')
117+
.map((content) => content.text)
118+
.join('\n')
119+
120+
// Extract tool calls from the assistant text
121+
const toolCallsText = extractToolCallsOnly(assistantText)
122+
123+
const { agentState: postAssistantTextAgentState } = yield {
124+
type: 'STEP_TEXT',
125+
text: toolCallsText,
126+
} as StepText
127+
128+
const postAssistantTextMessageHistory =
129+
postAssistantTextAgentState.messageHistory.slice(
130+
initialMessageHistoryLength,
131+
)
132+
const toolResults = postAssistantTextMessageHistory
133+
.filter((message) => message.role === 'tool')
134+
.flatMap((message) => message.content)
135+
.filter((content) => content.type === 'json')
136+
.map((content) => content.value)
137+
138+
yield {
139+
toolName: 'set_output',
140+
input: {
141+
output: {
142+
message: toolCallsText,
143+
toolResults,
144+
},
134145
},
135-
},
136-
includeToolCall: false,
137-
}
138-
},
139-
})
146+
includeToolCall: false,
147+
}
148+
149+
// Extract only tool calls from text, removing any commentary
150+
function extractToolCallsOnly(text: string): string {
151+
const toolExtractionPattern =
152+
/<codebuff_tool_call>[\s\S]*?<\/codebuff_tool_call>/g
153+
const matches: string[] = []
154+
155+
for (const match of text.matchAll(toolExtractionPattern)) {
156+
matches.push(match[0])
157+
}
158+
159+
return matches.join('\n')
160+
}
161+
},
162+
} satisfies Omit<AgentDefinition, 'id'>
163+
}
140164

141165
const definition = {
142166
...createCodeEditor({ model: 'opus' }),

cli/src/chat.tsx

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -588,22 +588,22 @@ export const Chat = ({
588588
const isWaitingForResponse = streamStatus === 'waiting'
589589
const isStreaming = streamStatus !== 'idle'
590590

591-
// When streaming completes, flush any pending bash commands into history
591+
// When streaming completes, flush any pending bash commands into history (ghost mode only)
592+
// Non-ghost mode commands are already in history and will be cleared when user sends next message
592593
useEffect(() => {
593594
if (
594595
!isStreaming &&
595596
!streamMessageIdRef.current &&
596597
!isChainInProgressRef.current &&
597598
pendingBashMessages.length > 0
598599
) {
599-
// Collect completed messages to flush
600-
const completedMessages = pendingBashMessages.filter(
601-
(msg) => !msg.isRunning,
600+
// Only flush ghost mode commands (those not already added to history) to UI
601+
const ghostModeMessages = pendingBashMessages.filter(
602+
(msg) => !msg.isRunning && !msg.addedToHistory,
602603
)
603-
if (completedMessages.length === 0) return
604-
605-
// Batch: add all to history, then clear all completed
606-
for (const msg of completedMessages) {
604+
605+
// Add ghost mode messages to UI history
606+
for (const msg of ghostModeMessages) {
607607
addBashMessageToHistory({
608608
command: msg.command,
609609
stdout: msg.stdout,
@@ -613,13 +613,17 @@ export const Chat = ({
613613
setMessages,
614614
})
615615
}
616-
// Batch remove all completed messages at once
617-
const completedIds = new Set(completedMessages.map((m) => m.id))
618-
useChatStore.setState((state) => ({
619-
pendingBashMessages: state.pendingBashMessages.filter(
620-
(m) => !completedIds.has(m.id),
621-
),
622-
}))
616+
617+
// Mark ghost mode messages as added to history (so they don't show as ghost UI)
618+
// but keep them in pendingBashMessages so they get sent to LLM with next user message
619+
if (ghostModeMessages.length > 0) {
620+
const ghostIds = new Set(ghostModeMessages.map((m) => m.id))
621+
useChatStore.setState((state) => ({
622+
pendingBashMessages: state.pendingBashMessages.map((m) =>
623+
ghostIds.has(m.id) ? { ...m, addedToHistory: true } : m,
624+
),
625+
}))
626+
}
623627
}
624628
}, [isStreaming, pendingBashMessages, setMessages])
625629

@@ -1234,14 +1238,16 @@ export const Chat = ({
12341238
/>
12351239
)
12361240
})}
1237-
{/* Pending bash messages as ghost messages */}
1238-
{pendingBashMessages.map((msg) => (
1239-
<PendingBashMessage
1240-
key={`pending-bash-${msg.id}`}
1241-
message={msg}
1242-
width={separatorWidth - 4}
1243-
/>
1244-
))}
1241+
{/* Pending bash messages as ghost messages (only show those not already in history) */}
1242+
{pendingBashMessages
1243+
.filter((msg) => !msg.addedToHistory)
1244+
.map((msg) => (
1245+
<PendingBashMessage
1246+
key={`pending-bash-${msg.id}`}
1247+
message={msg}
1248+
width={separatorWidth - 4}
1249+
/>
1250+
))}
12451251
</scrollbox>
12461252

12471253
<box

cli/src/commands/router.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ function executeBashCommand(
116116
return didUpdate ? { ...msg, blocks, isComplete: true } : msg
117117
}),
118118
)
119+
120+
// Also add to pending bash messages so the next user message includes this context for the LLM
121+
// Mark as already added to history to avoid duplicate UI entries
122+
useChatStore.getState().addPendingBashMessage({
123+
id,
124+
command,
125+
stdout,
126+
stderr,
127+
exitCode,
128+
isRunning: false,
129+
cwd: commandCwd,
130+
addedToHistory: true,
131+
})
119132
}
120133
})
121134
.catch((error) => {
@@ -154,6 +167,19 @@ function executeBashCommand(
154167
return didUpdate ? { ...msg, blocks, isComplete: true } : msg
155168
}),
156169
)
170+
171+
// Also add to pending bash messages so the next user message includes this context for the LLM
172+
// Mark as already added to history to avoid duplicate UI entries
173+
useChatStore.getState().addPendingBashMessage({
174+
id,
175+
command,
176+
stdout: '',
177+
stderr: errorMessage,
178+
exitCode: 1,
179+
isRunning: false,
180+
cwd: commandCwd,
181+
addedToHistory: true,
182+
})
157183
}
158184
})
159185
}

0 commit comments

Comments
 (0)