diff --git a/server/context_window_navigation_test.go b/server/context_window_navigation_test.go new file mode 100644 index 0000000000000000000000000000000000000000..685aefc60a2c15ca0732b139ec1c81fbfbdf83a4 --- /dev/null +++ b/server/context_window_navigation_test.go @@ -0,0 +1,84 @@ +package server + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +// TestContextWindowSizePreservedOnNavigation tests that context window size +// is correctly returned when loading different conversations +func TestContextWindowSizePreservedOnNavigation(t *testing.T) { + h := NewTestHarness(t) + defer h.Close() + + // Create first conversation and get a response + h.NewConversation("echo: first message", "/tmp") + resp1 := h.WaitResponse() + t.Logf("First conversation first response: %q", resp1) + + // Get context window size for first conversation + firstConvID := h.convID + firstConvSize := h.GetContextWindowSize() + t.Logf("First conversation context window size: %d", firstConvSize) + if firstConvSize == 0 { + t.Fatal("expected non-zero context window size for first conversation") + } + + // Create second conversation and get a response + h.NewConversation("echo: second message with much more text to ensure different context size", "/tmp") + resp2 := h.WaitResponse() + t.Logf("Second conversation first response: %q", resp2) + + secondConvID := h.convID + secondConvSize := h.GetContextWindowSize() + t.Logf("Second conversation context window size: %d", secondConvSize) + if secondConvSize == 0 { + t.Fatal("expected non-zero context window size for second conversation") + } + + // Now simulate "navigating" back to the first conversation by fetching it via GET + // This is what the UI does when switching conversations + req := httptest.NewRequest("GET", "/api/conversation/"+firstConvID, nil) + w := httptest.NewRecorder() + h.server.handleGetConversation(w, req, firstConvID) + + if w.Code != http.StatusOK { + t.Fatalf("GET first conversation returned %d: %s", w.Code, w.Body.String()) + } + + var resp StreamResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + t.Logf("First conversation on navigation: context_window_size=%d, messages=%d", + resp.ContextWindowSize, len(resp.Messages)) + + if resp.ContextWindowSize != firstConvSize { + t.Errorf("context_window_size mismatch on navigation: got %d, want %d", + resp.ContextWindowSize, firstConvSize) + } + + // Now navigate to second conversation + req = httptest.NewRequest("GET", "/api/conversation/"+secondConvID, nil) + w = httptest.NewRecorder() + h.server.handleGetConversation(w, req, secondConvID) + + if w.Code != http.StatusOK { + t.Fatalf("GET second conversation returned %d: %s", w.Code, w.Body.String()) + } + + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + t.Logf("Second conversation on navigation: context_window_size=%d, messages=%d", + resp.ContextWindowSize, len(resp.Messages)) + + if resp.ContextWindowSize != secondConvSize { + t.Errorf("context_window_size mismatch on navigation: got %d, want %d", + resp.ContextWindowSize, secondConvSize) + } +} diff --git a/server/testharness_test.go b/server/testharness_test.go index 2d1ece5409277c8450ce91a60446fb47e2b782fa..99e3306f57776fd23576177274860e97bd8482fe 100644 --- a/server/testharness_test.go +++ b/server/testharness_test.go @@ -85,6 +85,7 @@ func (h *TestHarness) NewConversation(msg, cwd string) *TestHarness { h.t.Fatalf("NewConversation: failed to parse response: %v", err) } h.convID = resp.ConversationID + h.responsesCount = 0 // Reset for new conversation return h } diff --git a/ui/src/components/ChatInterface.tsx b/ui/src/components/ChatInterface.tsx index bad79ffe8a1a216d31b059e75a6ce27fe755b570..4c38656c6ef6ffa0f2165e218f4e95ba88b82698 100644 --- a/ui/src/components/ChatInterface.tsx +++ b/ui/src/components/ChatInterface.tsx @@ -522,9 +522,9 @@ function ChatInterface({ const response = await api.getConversation(conversationId); setMessages(response.messages ?? []); setAgentWorking(Boolean(response.agent_working)); - if (typeof response.context_window_size === "number") { - setContextWindowSize(response.context_window_size); - } + // Always update context window size when loading a conversation. + // If omitted from response (due to omitempty when 0), default to 0. + setContextWindowSize(response.context_window_size ?? 0); if (onConversationUpdate) { onConversationUpdate(response.conversation); }