The trailing CR that submits orchestrator-injected input was written only 15ms after the body, inside TUI agents' paste-coalescing window, so codex (and other paste-detecting agents) intermittently swallowed it as a newline and left the message composed but unsent. Centralize the per-piece timing in a pure pieceWriteDelay helper: keep 15ms between body lines but give the final lone Enter a 100ms settle gap so the agent closes the preceding burst and registers the CR as submit. Covers send_input, send_message, timers, and the spawn initial prompt (all go through writeInput). Resolves the codex composer-submit TODO item.
91 lines
1.9 KiB
Go
91 lines
1.9 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestInputWritePiecesOnlySplitAgentEnters(t *testing.T) {
|
|
in := []byte("alpha\nbeta\rgamma")
|
|
for _, kind := range []ChildKind{KindTerminal, KindCommand} {
|
|
t.Run(string(kind), func(t *testing.T) {
|
|
got := inputWritePieces(kind, in)
|
|
if len(got) != 1 || !bytes.Equal(got[0], in) {
|
|
t.Fatalf("inputWritePieces(%s) = %#v, want one original chunk", kind, got)
|
|
}
|
|
})
|
|
}
|
|
|
|
got := inputWritePieces(KindAgent, in)
|
|
if len(got) != 5 {
|
|
t.Fatalf("agent pieces len = %d, want 5 (%#v)", len(got), got)
|
|
}
|
|
want := [][]byte{[]byte("alpha"), []byte("\n"), []byte("beta"), []byte("\r"), []byte("gamma")}
|
|
for i := range want {
|
|
if !bytes.Equal(got[i], want[i]) {
|
|
t.Fatalf("agent piece %d = %q, want %q", i, got[i], want[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPieceWriteDelay(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
index int
|
|
total int
|
|
piece []byte
|
|
want time.Duration
|
|
}{
|
|
{
|
|
name: "first piece",
|
|
index: 0,
|
|
total: 3,
|
|
piece: []byte("body"),
|
|
want: 0,
|
|
},
|
|
{
|
|
name: "middle body piece",
|
|
index: 1,
|
|
total: 3,
|
|
piece: []byte("body"),
|
|
want: agentInterPieceDelay,
|
|
},
|
|
{
|
|
name: "final carriage return submit",
|
|
index: 1,
|
|
total: 2,
|
|
piece: []byte("\r"),
|
|
want: agentSubmitSettleDelay,
|
|
},
|
|
{
|
|
name: "final newline submit",
|
|
index: 1,
|
|
total: 2,
|
|
piece: []byte("\n"),
|
|
want: agentSubmitSettleDelay,
|
|
},
|
|
{
|
|
name: "final non-enter piece",
|
|
index: 2,
|
|
total: 3,
|
|
piece: []byte("tail"),
|
|
want: agentInterPieceDelay,
|
|
},
|
|
{
|
|
name: "standalone enter fast path",
|
|
index: 0,
|
|
total: 1,
|
|
piece: []byte("\r"),
|
|
want: 0,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := pieceWriteDelay(tc.index, tc.total, tc.piece); got != tc.want {
|
|
t.Fatalf("pieceWriteDelay(%d, %d, %q) = %s, want %s", tc.index, tc.total, tc.piece, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|