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 lib/msgfmt/agent_readiness.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func isOpencodeAgentReadyForInitialPrompt(message string) bool {

func isCodexAgentReadyForInitialPrompt(message string) bool {
message = trimEmptyLines(message)
messageWithoutInputBox := removeCodexInputBox(message)
messageWithoutInputBox := removeCodexMessageBox(message)
return len(messageWithoutInputBox) != len(message)
}

Expand Down
9 changes: 4 additions & 5 deletions lib/msgfmt/message_box.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,11 @@ func removeMessageBox(msg string) string {
return strings.Join(lines, "\n")
}

func removeCodexInputBox(msg string) string {
func removeCodexMessageBox(msg string) string {
lines := strings.Split(msg, "\n")
// Remove the input box, we need to match the exact pattern, because thinking follows the same pattern of ▌ followed by text
if len(lines) >= 2 && strings.Contains(lines[len(lines)-2], "▌ Ask Codex to do anything") {
idx := len(lines) - 2
lines = append(lines[:idx], lines[idx+1:]...)
if len(lines) >= 3 && strings.Contains(lines[len(lines)-3], "›") {
idx := len(lines) - 3
lines = append(lines[:idx], lines[idx+2])
}
return strings.Join(lines, "\n")
}
Expand Down
6 changes: 3 additions & 3 deletions lib/msgfmt/msgfmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func IndexSubslice[T comparable](s, sub []T) int {
// Return the runes, the lines, and the rune to line location mapping.
func normalizeAndGetRuneLineMapping(msgRaw string) ([]rune, []string, []int) {
msgLines := strings.Split(msgRaw, "\n")
msgRuneLineLocations := []int{}
runes := []rune{}
var msgRuneLineLocations []int
var runes []rune
for lineIdx, line := range msgLines {
for _, r := range line {
if !strings.ContainsRune(WhiteSpaceChars, r) {
Expand Down Expand Up @@ -256,7 +256,7 @@ func formatGenericMessage(message string, userInput string, agentType AgentType)

func formatCodexMessage(message string, userInput string) string {
message = RemoveUserInput(message, userInput, AgentTypeCodex)
message = removeCodexInputBox(message)
message = removeCodexMessageBox(message)
message = trimEmptyLines(message)
return message
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
/diff - show git diff (including untracked files)
/prompts - show example prompts

⏎ send Shift+⏎ newline Ctrl+C quit
gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
5 changes: 3 additions & 2 deletions lib/msgfmt/testdata/format/codex/first_message/msg.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
/diff - show git diff (including untracked files)
/prompts - show example prompts

▌ Ask Codex to do anything
⏎ send Shift+⏎ newline Ctrl+C quit
› A for Apple

gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ The `formatCodexMessage` function is defined in:

`lib/msgfmt/msgfmt.go` (around line 219)

Ctrl+C again to quit 12437 tokens used 96% context left
gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
5 changes: 3 additions & 2 deletions lib/msgfmt/testdata/format/codex/multi-line-input/msg.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ The `formatCodexMessage` function is defined in:

`lib/msgfmt/msgfmt.go` (around line 219)

▌ Ask Codex to do anything
Ctrl+C again to quit 12437 tokens used 96% context left
› Ask Codex to do anything

gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
codex
There are 2 untracked files (`.env` and `forge.yaml`).

⏎ send Ctrl+J newline Ctrl+C quit 18724 tokens used 96% context left
gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
5 changes: 3 additions & 2 deletions lib/msgfmt/testdata/format/codex/second_message/msg.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ How many untracked files are there?
codex
There are 2 untracked files (`.env` and `forge.yaml`).

▌ Ask Codex to do anything
⏎ send Ctrl+J newline Ctrl+C quit 18724 tokens used 96% context left
› Ask Codex to do anything

gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
5 changes: 3 additions & 2 deletions lib/msgfmt/testdata/initialization/codex/ready/msg.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
/diff - show git diff (including untracked files)
/prompts - show example prompts

▌ Ask Codex to do anything
⏎ send Shift+⏎ newline Ctrl+C quit
› Refactor this Code...

gpt-5.1-codex-max default · 100% left · ~/Documents/work/coder_o…
23 changes: 12 additions & 11 deletions lib/screentracker/pty_conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ type PTYConversation struct {
// Set under lock in the snapshot loop when signaling, cleared under
// lock in the send loop after sendMessage returns.
sendingMessage bool
// writingMessage is true while writeStabilize is executing.
// When true, updateLastAgentMessageLocked skips updates to avoid capturing terminal echo.
writingMessage bool
// stableSignal is used by the snapshot loop to signal the send loop
// when the agent is stable and there are items in the outbound queue.
stableSignal chan struct{}
Expand Down Expand Up @@ -177,6 +180,7 @@ func NewPTY(ctx context.Context, cfg PTYConversationConfig, emitter Emitter) *PT
dirty: false,
userSentMessageAfterLoadState: false,
loadStateStatus: LoadStatePending,
writingMessage: false,
}
if c.cfg.ReadyForInitialPrompt == nil {
c.cfg.ReadyForInitialPrompt = func(string) bool { return true }
Expand Down Expand Up @@ -295,6 +299,9 @@ func (c *PTYConversation) lastMessage(role ConversationRole) ConversationMessage

// caller MUST hold c.lock
func (c *PTYConversation) updateLastAgentMessageLocked(screen string, timestamp time.Time) {
if c.writingMessage {
return
}
agentMessage := screenDiff(c.screenBeforeLastUserMessage, screen, c.cfg.AgentType)
lastUserMessage := c.lastMessage(ConversationRoleUser)
var toolCalls []string
Expand Down Expand Up @@ -380,23 +387,17 @@ func (c *PTYConversation) sendMessage(ctx context.Context, messageParts ...Messa
screenBeforeMessage := c.cfg.AgentIO.ReadScreen()
now := c.cfg.Clock.Now()
c.updateLastAgentMessageLocked(screenBeforeMessage, now)
c.writingMessage = true
c.lock.Unlock()

if err := c.writeStabilize(ctx, messageParts...); err != nil {
c.lock.Lock()
defer c.lock.Unlock()
c.writingMessage = false
return xerrors.Errorf("failed to send message: %w", err)
}

c.lock.Lock()
// Re-apply the pre-send agent message from the screen captured before
// the write. While the lock was released during writeStabilize, the
// snapshot loop continued taking snapshots and calling
// updateLastAgentMessageLocked with whatever was on screen at each
// tick (typically echoed user input or intermediate terminal state).
// Those updates corrupt the agent message for this turn. Restoring it
// here ensures the conversation history is correct. The next line sets
// screenBeforeLastUserMessage so the *next* agent message will be
// diffed relative to the pre-send screen.
c.updateLastAgentMessageLocked(screenBeforeMessage, now)
c.screenBeforeLastUserMessage = screenBeforeMessage
c.messages = append(c.messages, ConversationMessage{
Id: len(c.messages),
Expand All @@ -405,7 +406,7 @@ func (c *PTYConversation) sendMessage(ctx context.Context, messageParts ...Messa
Time: now,
})
c.userSentMessageAfterLoadState = true

c.writingMessage = false
c.lock.Unlock()
return nil
}
Expand Down