Skip to content

Commit 738cf99

Browse files
intel352claude
andcommitted
fix: PTY CLI providers use Chat() instead of Stream() for chat handler
PTY providers (claude_code, copilot_cli, codex_cli, gemini_cli, cursor_cli) don't support the interactive Stream() path well — their rich TUI output breaks prompt detection. The chat handler now detects PTY providers by name suffix and uses Chat() (non-interactive -p flag) with fake-streaming (emit full response as single token event). Verified: Claude Code through ratchet returns correct responses for both simple queries ("What is 2+2?") and code generation tasks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 61b99b1 commit 738cf99

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

internal/daemon/chat.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,38 @@ func (s *Service) handleChat(ctx context.Context, sessionID, userMessage string,
176176
debugLog("[chat] session=%s sending %d messages: %s", sessionID, len(messages), string(msgJSON))
177177
}
178178

179+
// PTY CLI providers (claude_code, copilot_cli, etc.) don't support streaming
180+
// well — their interactive PTY mode requires complex prompt detection.
181+
// Use Chat() (non-interactive) for these and fake-stream the response.
182+
isPTYProvider := strings.HasSuffix(prov.Name(), "_code") || strings.HasSuffix(prov.Name(), "_cli")
183+
if isPTYProvider {
184+
resp, chatErr := prov.Chat(ctx, messages, nil)
185+
if chatErr != nil {
186+
if isAuthError(chatErr) {
187+
return sendAuthError(stream, session.Provider, chatErr.Error())
188+
}
189+
return sendError(stream, "provider chat: "+chatErr.Error())
190+
}
191+
// Save user message after successful provider call.
192+
if err := s.saveMessage(ctx, sessionID, "user", userMessage, "", ""); err != nil {
193+
log.Printf("save user message: %v", err)
194+
}
195+
// Emit the full response as a single token event.
196+
if resp != nil && resp.Content != "" {
197+
if err := stream.Send(&pb.ChatEvent{
198+
Event: &pb.ChatEvent_Token{Token: &pb.TokenDelta{Content: resp.Content}},
199+
}); err != nil {
200+
return err
201+
}
202+
if err := s.saveMessage(ctx, sessionID, "assistant", resp.Content, "", ""); err != nil {
203+
log.Printf("save assistant message: %v", err)
204+
}
205+
}
206+
return stream.Send(&pb.ChatEvent{
207+
Event: &pb.ChatEvent_Complete{Complete: &pb.SessionComplete{Summary: "done"}},
208+
})
209+
}
210+
179211
// Stream from provider (save user message AFTER successful stream start,
180212
// so failed requests don't pollute conversation history).
181213
eventCh, err := prov.Stream(ctx, messages, nil) // tools will be added in later task

0 commit comments

Comments
 (0)