From 45d298ba0b9b36dbfd1fdc2dd868ca447776bc52 Mon Sep 17 00:00:00 2001 From: "Naveen R. Iyer" Date: Sat, 4 Jul 2026 21:15:23 -0500 Subject: [PATCH] clamp chat history summary to the column length so completion status is saved The summary column is length-bounded; storing an over-long value made the whole history update fail server-side, discarding the status change carried by the same request, so a completed run stayed marked ongoing. Clamp the summary in the three history update payloads through a shared helper, and clamp defensively on the server with the limit derived from the column definition so the two cannot drift apart. The full text is unaffected; it is preserved in the run's end step. Signed-off-by: Naveen R. Iyer --- .../app/domains/chat/api/history_controller.py | 6 ++++++ src/store/chatStore.ts | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/server/app/domains/chat/api/history_controller.py b/server/app/domains/chat/api/history_controller.py index d717af24f..26574ef11 100644 --- a/server/app/domains/chat/api/history_controller.py +++ b/server/app/domains/chat/api/history_controller.py @@ -179,6 +179,12 @@ async def update_chat_history( raise HTTPException(status_code=403, detail="You are not allowed to update this chat history") update_data = data.model_dump(exclude_unset=True) + # The summary column is length-bounded; clamp defensively so an + # over-long value from any client truncates instead of failing the + # whole update (which would also discard the status change). + if isinstance(update_data.get("summary"), str): + summary_limit = ChatHistory.summary.type.length + update_data["summary"] = update_data["summary"][:summary_limit] history.update_fields(update_data) if "project_name" in update_data: _sync_project_display_name( diff --git a/src/store/chatStore.ts b/src/store/chatStore.ts index 6b8a112c8..4f3f1c4dd 100644 --- a/src/store/chatStore.ts +++ b/src/store/chatStore.ts @@ -58,6 +58,14 @@ import { legacySpaceIdForUser, useSpaceStore } from './spaceStore'; const API_CODE_TRIAL_LIMIT = '22'; const PROJECT_CONTEXT_MAX_CHARS = 24_000; const PROJECT_CONTEXT_MAX_RUNS = 8; +// chat_history.summary is a bounded database column; an over-long value +// makes the whole history update fail server-side, which also discards the +// status change carried by the same request (a completed run then stays +// "ongoing"). Clamp before sending; the full text still lives in the run's +// end step. +const MAX_CHAT_HISTORY_SUMMARY_LENGTH = 1024; +const clampHistorySummary = (value: string | undefined | null): string => + (value ?? '').slice(0, MAX_CHAT_HISTORY_SUMMARY_LENGTH); type ConfirmedUserPromptSources = { lastMessageContent?: unknown; @@ -2367,7 +2375,9 @@ const chatStore = (initial?: Partial) => agentMessages.data!.summary_task?.split('|')[0] || ''; const obj = { project_name: projectName, - summary: agentMessages.data!.summary_task?.split('|')[1] || '', + summary: clampHistorySummary( + agentMessages.data!.summary_task?.split('|')[1] + ), status: 1, tokens: getTokens(currentTaskId), }; @@ -2750,7 +2760,9 @@ const chatStore = (initial?: Partial) => tasks[currentTaskId].summaryTask.split('|')[0]; const obj = { project_name: projectName, - summary: tasks[currentTaskId].summaryTask.split('|')[1], + summary: clampHistorySummary( + tasks[currentTaskId].summaryTask.split('|')[1] + ), status: 1, tokens: getTokens(currentTaskId), }; @@ -3618,7 +3630,7 @@ const chatStore = (initial?: Partial) => const projectName = parts[0] || ''; const obj = { project_name: projectName, - summary: completionSummary, + summary: clampHistorySummary(completionSummary), status: wasStoppedByUser ? 1 : 2, tokens: getTokens(currentTaskId), };