From c1e209a064d994bbed9baffe297e1b5729bea5ab Mon Sep 17 00:00:00 2001 From: agent-exec-transport Date: Fri, 23 Oct 2026 13:17:56 +0000 Subject: [PATCH] agent-exec transport 5b732130457a04d9a20ff740653539b8/feb6b3f216f8 --- .../multi-instance/multi_instance_test.go | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/integration/multi-instance/multi_instance_test.go b/tests/integration/multi-instance/multi_instance_test.go index 13018213..cff902a4 100644 --- a/tests/integration/multi-instance/multi_instance_test.go +++ b/tests/integration/multi-instance/multi_instance_test.go @@ -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) {