From 42819c45d9c7b21bb666715d26d29cc3299a3c00 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 10 Nov 2026 18:44:47 -0800 Subject: [PATCH] feat(sessions): let a session message steer the running turn SessionMessageRequest gains Steering. When the session has a turn in flight, the server takes a steering message into that turn at its next step instead of queueing it for a turn of its own; the running turn then answers it, so its answer is read from the session's event stream rather than from the message's own invocation. The field is omitted when false, so existing requests are unchanged. --- sessions_message.go | 6 ++++++ sessions_message_test.go | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/sessions_message.go b/sessions_message.go index 3c1bd3c..2baa5c1 100644 --- a/sessions_message.go +++ b/sessions_message.go @@ -61,6 +61,12 @@ type SessionMessageRequest struct { // agent answer through its reply tool only; check the ack to see whether // the server accepted it. DeliveryMode string `json:"delivery_mode,omitempty"` + // Steering asks the session's running turn to take this message in at its + // next step instead of queueing it for a turn of its own. Its answer then + // comes from that running turn, so read it from the session's event stream + // rather than only from this message's own invocation. Ignored when no turn + // is running. + Steering bool `json:"steering,omitempty"` // TeamID binds a new session to a team (0 = account scope). TeamID int64 `json:"team_id,omitempty"` // EnvironmentKind selects the execution environment: "cloud" or "byoc". diff --git a/sessions_message_test.go b/sessions_message_test.go index 7ba0840..b5d1bcd 100644 --- a/sessions_message_test.go +++ b/sessions_message_test.go @@ -35,6 +35,9 @@ func TestSessionsWriteMessageSendsRequestAndDecodesAck(t *testing.T) { if _, ok := body["team_id"]; ok { t.Errorf("zero team_id must be omitted: %v", body) } + if _, ok := body["steering"]; ok { + t.Errorf("false steering must be omitted: %v", body) + } w.WriteHeader(http.StatusAccepted) _, _ = io.WriteString(w, `{"request_id":"r1","data":{"session_id":"s1","invocation_id":"i1","root_invocation_id":"i1","delivery_mode":"reply_only"}}`) }) @@ -58,6 +61,23 @@ func TestSessionsWriteMessageSendsRequestAndDecodesAck(t *testing.T) { } } +func TestSessionsWriteMessageSendsSteering(t *testing.T) { + c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + var body map[string]any + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Fatal(err) + } + if body["steering"] != true { + t.Errorf("steering = %v, want true", body["steering"]) + } + w.WriteHeader(http.StatusAccepted) + _, _ = io.WriteString(w, `{"request_id":"r1","data":{"session_id":"s1","invocation_id":"i2","root_invocation_id":"i2"}}`) + }) + if _, _, err := c.Sessions.WriteMessage(context.Background(), &SessionMessageRequest{SessionID: "s1", Query: "and now?", Steering: true}); err != nil { + t.Fatalf("WriteMessage: %v", err) + } +} + func TestSessionsWriteMessageSurfacesAPIError(t *testing.T) { c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest)