Skip to content

Commit 6cbd2a0

Browse files
authored
Merge dc013e9 into 2cb482d
2 parents 2cb482d + dc013e9 commit 6cbd2a0

4 files changed

Lines changed: 25 additions & 6 deletions

File tree

src/gateway/session-utils.fs.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,6 +2008,20 @@ describe("readSessionPreviewItemsFromTranscript", () => {
20082008
expect(result[0]?.text.endsWith("...")).toBe(true);
20092009
});
20102010

2011+
test("keeps preview text valid when the limit bisects an emoji", () => {
2012+
const sessionId = "preview-truncate-utf16";
2013+
const lines = [
2014+
JSON.stringify({
2015+
message: { role: "assistant", content: `${"t".repeat(196)}🚀xyz` },
2016+
}),
2017+
];
2018+
writeTranscriptLines(sessionId, lines);
2019+
2020+
expect(readPreview(sessionId, 1, 200)).toEqual([
2021+
{ role: "assistant", text: `${"t".repeat(196)}...` },
2022+
]);
2023+
});
2024+
20112025
test("strips inline directives from preview items", () => {
20122026
const sessionId = "preview-strip-inline-directives";
20132027
const lines = [

src/gateway/session-utils.fs.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import { jsonUtf8Bytes } from "../infra/json-utf8-bytes.js";
2121
import { hasInterSessionUserProvenance } from "../sessions/input-provenance.js";
2222
import { extractAssistantVisibleText } from "../shared/chat-message-content.js";
23+
import { truncateUtf16Safe } from "../utils.js";
2324
import { estimateStringChars, estimateTokensFromChars } from "../utils/cjk-chars.js";
2425
import { stripInlineDirectiveTagsForDisplay } from "../utils/directive-tags.js";
2526
import { extractToolCallNames, hasToolCall } from "../utils/transcript-tools.js";
@@ -1919,13 +1920,11 @@ function normalizeRole(role: string | undefined, isTool: boolean): SessionPrevie
19191920
}
19201921

19211922
function truncatePreviewText(text: string, maxChars: number): string {
1922-
if (maxChars <= 0 || text.length <= maxChars) {
1923+
if (text.length <= maxChars) {
19231924
return text;
19241925
}
1925-
if (maxChars <= 3) {
1926-
return text.slice(0, maxChars);
1927-
}
1928-
return `${text.slice(0, maxChars - 3)}...`;
1926+
// The preview entry point clamps maxChars to at least 20, so the suffix budget stays positive.
1927+
return `${truncateUtf16Safe(text, maxChars - 3)}...`;
19291928
}
19301929

19311930
function extractPreviewText(message: TranscriptPreviewMessage): string | null {

src/gateway/session-utils.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2560,6 +2560,11 @@ describe("deriveSessionTitle", () => {
25602560
expect(result.endsWith("…")).toBe(true);
25612561
});
25622562

2563+
test("keeps a derived title valid when the limit bisects an emoji", () => {
2564+
const entry = { sessionId: "abc123", updatedAt: Date.now() } as SessionEntry;
2565+
expect(deriveSessionTitle(entry, `${"t".repeat(58)}🚀 extra`)).toBe(`${"t".repeat(58)}…`);
2566+
});
2567+
25632568
test("truncates at word boundary when possible", () => {
25642569
const entry = {
25652570
sessionId: "abc123",

src/gateway/session-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import {
9393
resolveAvatarMime,
9494
} from "../shared/avatar-policy.js";
9595
import { resolveNonNegativeNumber } from "../shared/number-coercion.js";
96+
import { truncateUtf16Safe } from "../utils.js";
9697
import { normalizeSessionDeliveryFields } from "../utils/delivery-context.shared.js";
9798
import type { ModelCostConfig } from "../utils/usage-format.js";
9899
import { estimateUsageCost, resolveModelCostConfig } from "../utils/usage-format.js";
@@ -229,7 +230,7 @@ function truncateTitle(text: string, maxLen: number): string {
229230
if (text.length <= maxLen) {
230231
return text;
231232
}
232-
const cut = text.slice(0, maxLen - 1);
233+
const cut = truncateUtf16Safe(text, maxLen - 1);
233234
const lastSpace = cut.lastIndexOf(" ");
234235
if (lastSpace > maxLen * 0.6) {
235236
return cut.slice(0, lastSpace) + "…";

0 commit comments

Comments
 (0)