Skip to content

Commit 886a836

Browse files
intel352claude
andcommitted
feat: Stream() uses interactive PTY (vt10x) as primary path
Interactive PTY is now the primary Stream() path for all CLI providers, not just a fallback. This maintains session context across multiple calls — essential for multi-turn conversations and tool execution. Resolution order: 1. Interactive PTY (vt10x) — keeps session alive, multi-turn 2. JSON streaming — structured events, stateless fallback 3. Non-interactive exec — simplest, stateless last resort Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3f530ad commit 886a836

1 file changed

Lines changed: 13 additions & 12 deletions

File tree

genkit/pty_provider.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ func (p *ptyProvider) Chat(ctx context.Context, messages []provider.Message, _ [
9797
return &provider.Response{Content: content}, nil
9898
}
9999

100-
// Stream runs the CLI tool and streams response events.
101-
// If the adapter supports StreamingArgs (JSON streaming), uses that for reliable
102-
// structured output. Otherwise falls back to non-interactive exec with line-by-line reading.
100+
// Stream uses the interactive PTY (vt10x) path to maintain session context
101+
// across multiple calls. The PTY session is kept alive for multi-turn conversation.
102+
// Falls back to JSON streaming or non-interactive exec if the interactive session fails.
103103
func (p *ptyProvider) Stream(ctx context.Context, messages []provider.Message, _ []provider.ToolDef) (<-chan provider.StreamEvent, error) {
104104
msg := flattenMessages(messages)
105105

@@ -108,17 +108,18 @@ func (p *ptyProvider) Stream(ctx context.Context, messages []provider.Message, _
108108
go func() {
109109
defer close(ch)
110110

111-
// Prefer structured JSON streaming if the adapter supports it.
112-
if args := p.adapter.StreamingArgs(msg); args != nil {
113-
if err := p.streamJSON(ctx, args, ch); err != nil {
111+
// Primary: interactive PTY session via vt10x virtual terminal.
112+
if err := p.streamInteractive(ctx, msg, ch); err != nil {
113+
// If interactive fails, try JSON streaming as fallback.
114+
if args := p.adapter.StreamingArgs(msg); args != nil {
115+
if jsonErr := p.streamJSON(ctx, args, ch); jsonErr == nil {
116+
return
117+
}
118+
}
119+
// Last resort: non-interactive exec.
120+
if fallbackErr := p.streamFallback(ctx, msg, ch); fallbackErr != nil {
114121
ch <- provider.StreamEvent{Type: "error", Error: err.Error()}
115122
}
116-
return
117-
}
118-
119-
// Fallback: run non-interactive and emit result as single event.
120-
if err := p.streamFallback(ctx, msg, ch); err != nil {
121-
ch <- provider.StreamEvent{Type: "error", Error: err.Error()}
122123
}
123124
}()
124125

0 commit comments

Comments
 (0)