agent-exec transport 5b732130457a04d9a20ff740653539b8/feb6b3f216f8
ci / svc-realcombo (pull_request) Successful in 4m44s
ci / test (pull_request) Successful in 10m5s
ci / test-pg (pull_request) Successful in 15m59s

This commit is contained in:
agent-exec-transport
2026-10-23 13:17:56 +00:00
parent 11002fe208
commit c1e209a064
@@ -725,10 +725,28 @@ func (e *env) setupConversation(t *testing.T, uc *userClient) (string, string) {
// postMessage 先等会话空闲(上一 run 终态),再提交消息——#hitl 需要一个
// 可占用的空闲会话;普通消息同样避免撞上上一 run 的收口窗口。
//
// waitConversationIdle 与 POST 之间存在 TOCTOU:观察 idle 后、POST 到达前,
// 上一条消息的 run 可能才异步开始(新依赖管线启动更慢),服务端正确返回
// 409 "conversation already has an in-flight run"。对这类 409 有界重试
// (再 wait + POST,最多 3 次);其他状态码仍立即 Fatal,不吞错误。
func (e *env) postMessage(t *testing.T, c *http.Client, convID, text string) {
t.Helper()
const maxInFlightRetries = 3
path := "/api/conversations/" + convID + "/messages"
body := map[string]any{"text": text}
e.waitConversationIdle(t, c, convID, 20*time.Second)
e.do(t, c, "POST", "/api/conversations/"+convID+"/messages", map[string]any{"text": text}, nil)
for attempt := 0; ; attempt++ {
code, raw := e.raw(t, c, "POST", path, body)
if code == http.StatusOK {
return
}
if code == http.StatusConflict && strings.Contains(raw, "in-flight") && attempt < maxInFlightRetries {
e.waitConversationIdle(t, c, convID, 20*time.Second)
continue
}
t.Fatalf("POST %s: status %d, body %.300s", path, code, raw)
}
}
func (e *env) waitConversationIdle(t *testing.T, c *http.Client, convID string, within time.Duration) {