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
4 changes: 3 additions & 1 deletion cmd/attach/attach.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,8 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/coder/agentapi/lib/httpapi"
"github.com/coder/agentapi/lib/util"
"github.com/coder/quartz"
"github.com/spf13/cobra"
sse "github.com/tmaxmax/go-sse"
"golang.org/x/term"
Expand DownExpand Up@@ -213,7 +215,7 @@ func runAttach(remoteUrl string) error {
p.Send(finishMsg{})
select {
case <-pErrCh:
case <-time.After(1 * time.Second):
case <-util.After(quartz.NewReal(), 1*time.Second):
}

return err
Expand Down
12 changes: 6 additions & 6 deletions lib/httpapi/server.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,7 +41,7 @@ type Server struct {
srv *http.Server
mu sync.RWMutex
logger *slog.Logger
conversation *st.Conversation
conversation *st.PTYConversation

@35C4n0r35C4n0rJan 23, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnstcn Shouldn't this be *st.Conversation ? (It's possible that I'm lacking the context/motive behind this refactor, If we have extracted an interface, shouldn't we be using it rather than coupling this to the concrete implementation ?)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should, but there's some more coupling to be disentangled here before we can do that.

agentio *termexec.Process
agentType mf.AgentType
emitter *EventEmitter
Expand DownExpand Up@@ -244,7 +244,7 @@ func NewServer(ctx context.Context, config ServerConfig) (*Server, error) {
return mf.FormatToolCall(config.AgentType, message)
}

conversation := st.NewConversation(ctx, st.ConversationConfig{
conversation := st.NewPTY(ctx, st.PTYConversationConfig{
AgentType: config.AgentType,
AgentIO: config.Process,
Clock: config.Clock,
Expand DownExpand Up@@ -337,7 +337,7 @@ func sseMiddleware(ctx huma.Context, next func(huma.Context)) {
}

func (s *Server) StartSnapshotLoop(ctx context.Context) {
s.conversation.StartSnapshotLoop(ctx)
s.conversation.Start(ctx)
go func() {
ticker := s.clock.NewTicker(snapshotInterval)
defer ticker.Stop()
Expand All@@ -346,7 +346,7 @@ func (s *Server) StartSnapshotLoop(ctx context.Context) {

// Send initial prompt when agent becomes stable for the first time
if !s.conversation.InitialPromptSent && convertStatus(currentStatus) == AgentStatusStable {
if err := s.conversation.SendMessage(FormatMessage(s.agentType, s.conversation.InitialPrompt)...); err != nil {
if err := s.conversation.Send(FormatMessage(s.agentType, s.conversation.InitialPrompt)...); err != nil {
s.logger.Error("Failed to send initial prompt", "error", err)
} else {
s.conversation.InitialPromptSent = true
Expand All@@ -357,7 +357,7 @@ func (s *Server) StartSnapshotLoop(ctx context.Context) {
}
s.emitter.UpdateStatusAndEmitChanges(currentStatus, s.agentType)
s.emitter.UpdateMessagesAndEmitChanges(s.conversation.Messages())
s.emitter.UpdateScreenAndEmitChanges(s.conversation.Screen())
s.emitter.UpdateScreenAndEmitChanges(s.conversation.Text())

select {
case <-ctx.Done():
Expand DownExpand Up@@ -461,7 +461,7 @@ func (s *Server) createMessage(ctx context.Context, input *MessageRequest) (*Mes

switch input.Body.Type {
case MessageTypeUser:
if err := s.conversation.SendMessage(FormatMessage(s.agentType, input.Body.Content)...); err != nil {
if err := s.conversation.Send(FormatMessage(s.agentType, input.Body.Content)...); err != nil {
return nil, xerrors.Errorf("failed to send message: %w", err)
}
case MessageTypeRaw:
Expand Down
Loading