70 lines
2.7 KiB
Go
70 lines
2.7 KiB
Go
package agents
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
bracketedPasteStart = "\x1b[200~"
|
|
bracketedPasteEnd = "\x1b[201~"
|
|
)
|
|
|
|
// PassthroughStdinChunk is one PTY stdin write planned for a passthrough prompt.
|
|
// DelayBefore is sleep applied before writing this chunk; it is zero on the first
|
|
// chunk and inherits PassthroughConfig.SubmitDelay on subsequent chunks. Splitting
|
|
// the prompt body from the submit byte with a delay defeats Ink-style paste-burst
|
|
// detection in TUIs like Claude Code (see PassthroughConfig.SubmitDelay).
|
|
type PassthroughStdinChunk struct {
|
|
Data string
|
|
DelayBefore time.Duration
|
|
}
|
|
|
|
// PassthroughSubmitSequence returns the byte sequence to append after passthrough
|
|
// stdin text. Empty SubmitSequence inherits DefaultPassthroughSubmitSequence ("\r").
|
|
func PassthroughSubmitSequence(cfg PassthroughConfig) string {
|
|
return EffectiveSubmitSequence(cfg.SubmitSequence)
|
|
}
|
|
|
|
// BuildPassthroughPayload assembles the bytes for one atomic PTY stdin write.
|
|
// Multi-line prompts use bracketed paste so embedded newlines are not treated as
|
|
// premature Enter presses, unless DisableBracketedPaste is set (Claude Code).
|
|
func BuildPassthroughPayload(prompt string, cfg PassthroughConfig) string {
|
|
submit := PassthroughSubmitSequence(cfg)
|
|
if cfg.DisableBracketedPaste || !strings.Contains(prompt, "\n") {
|
|
return prompt + submit
|
|
}
|
|
return bracketedPasteStart + prompt + bracketedPasteEnd + submit
|
|
}
|
|
|
|
// PlanPassthroughStdinChunks plans PTY stdin writes for a passthrough prompt.
|
|
// When SubmitDelay > 0 (Claude path) the prompt body and submit byte are emitted
|
|
// as two chunks with DelayBefore set on the submit chunk so it arrives as a
|
|
// discrete keystroke. Otherwise a single atomic write is returned, preserving
|
|
// today's behavior for TUIs that handle prompt+submit in one read (Cursor, Codex,
|
|
// OpenCode).
|
|
func PlanPassthroughStdinChunks(prompt string, cfg PassthroughConfig) []PassthroughStdinChunk {
|
|
if cfg.SubmitDelay > 0 {
|
|
body := prompt
|
|
if !cfg.DisableBracketedPaste && strings.Contains(prompt, "\n") {
|
|
body = bracketedPasteStart + prompt + bracketedPasteEnd
|
|
}
|
|
return []PassthroughStdinChunk{
|
|
{Data: body},
|
|
{Data: PassthroughSubmitSequence(cfg), DelayBefore: cfg.SubmitDelay},
|
|
}
|
|
}
|
|
return []PassthroughStdinChunk{{Data: BuildPassthroughPayload(prompt, cfg)}}
|
|
}
|
|
|
|
// PlanPassthroughStdinWrites is the legacy string-only view of PlanPassthroughStdinChunks.
|
|
// Retained for tests and call sites that don't need to honor per-chunk delays.
|
|
func PlanPassthroughStdinWrites(prompt string, cfg PassthroughConfig) []string {
|
|
chunks := PlanPassthroughStdinChunks(prompt, cfg)
|
|
out := make([]string, len(chunks))
|
|
for i, c := range chunks {
|
|
out[i] = c.Data
|
|
}
|
|
return out
|
|
}
|