From 94d2799788e4194ca511008c817cc7e4b2ace3a0 Mon Sep 17 00:00:00 2001 From: mds Date: Thu, 6 Aug 2026 22:32:00 +0700 Subject: [PATCH] feat(http): support persistent sessions via X-GoClaw-User-Id header (#713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When X-GoClaw-User-Id header is provided, build a stable session key using BuildSessionKey() — same canonical format as Telegram, Discord, Slack channels. This allows HTTP API clients to maintain conversation history across multiple requests. Without the header, behavior is unchanged (random session per request). Session key format: With userId: agent:{id}:http:direct:{userId} (persistent) Without: agent:{id}:http-{random} (stateless) Co-authored-by: Claude Code --- internal/http/chat_completions.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/http/chat_completions.go b/internal/http/chat_completions.go index 1891ade8..a226de4b 100644 --- a/internal/http/chat_completions.go +++ b/internal/http/chat_completions.go @@ -156,12 +156,14 @@ func (h *ChatCompletionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques } runID := uuid.NewString() - // Include userID in session key for multi-tenant isolation - sessionSuffix := "http-" + runID[:8] + // Build stable session key per user — same user+agent always reuses the same session + var sessionKey string if userID != "" { - sessionSuffix = "http-" + userID + "-" + runID[:8] + sessionKey = sessions.BuildSessionKey(agentID, "http", sessions.PeerDirect, userID) + } else { + // No user ID — fallback to random session (stateless) + sessionKey = sessions.SessionKey(agentID, "http-"+runID[:8]) } - sessionKey := sessions.SessionKey(agentID, sessionSuffix) slog.Info("chat completions request", "agent", agentID, "stream", req.Stream, "user", userID)