Skip to content

Commit a8d0879

Browse files
fix(text): keep five src-side text truncations UTF-16 safe
1 parent 4be6fa7 commit a8d0879

4 files changed

Lines changed: 8 additions & 5 deletions

File tree

src/auto-reply/fallback-state.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { formatRawAssistantErrorForUi } from "../agents/embedded-agent-helpers.j
44
import { areRuntimeModelRefsEquivalent } from "../agents/model-runtime-aliases.js";
55
import type { OpenClawConfig } from "../config/types.openclaw.js";
66
import type { FallbackNoticeState } from "../status/fallback-notice-state.js";
7+
import { truncateUtf16Safe } from "../utils.js";
78
import { formatProviderModelRef } from "./model-runtime.js";
89
import type { RuntimeFallbackAttempt } from "./reply/agent-runner-execution.js";
910

@@ -29,7 +30,7 @@ function truncateFallbackReasonPart(value: string, max = FALLBACK_REASON_PART_MA
2930
if (text.length <= max) {
3031
return text;
3132
}
32-
return `${text.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
33+
return `${truncateUtf16Safe(text, Math.max(0, max - 1)).trimEnd()}…`;
3334
}
3435

3536
function formatFallbackAttemptErrorPreview(attempt: RuntimeFallbackAttempt): string | undefined {

src/auto-reply/reply/conversation-label-generator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import type { OpenClawConfig } from "../../config/types.openclaw.js";
88
import { logVerbose } from "../../globals.js";
99
import type { TextContent } from "../../llm/types.js";
10+
import { truncateUtf16Safe } from "../../utils.js";
1011

1112
const DEFAULT_MAX_LABEL_LENGTH = 128;
1213
// Reasoning models spend output tokens before emitting the short visible label.
@@ -114,7 +115,7 @@ export async function generateConversationLabel(
114115
return null;
115116
}
116117

117-
return text.slice(0, maxLength);
118+
return truncateUtf16Safe(text, maxLength);
118119
} catch (err) {
119120
logVerbose(`conversation-label-generator: completion failed: ${String(err)}`);
120121
return null;

src/cli/plugins-list-format.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text.js";
33
import { theme } from "../../packages/terminal-core/src/theme.js";
44
import type { PluginRecord } from "../plugins/registry.js";
5-
import { shortenHomeInString } from "../utils.js";
5+
import { shortenHomeInString, truncateUtf16Safe } from "../utils.js";
66

77
export function formatPluginLine(plugin: PluginRecord, verbose = false): string {
88
const status =
@@ -16,7 +16,7 @@ export function formatPluginLine(plugin: PluginRecord, verbose = false): string
1616
const desc = plugin.description
1717
? theme.muted(
1818
plugin.description.length > 60
19-
? `${plugin.description.slice(0, 57)}...`
19+
? `${truncateUtf16Safe(plugin.description, 57)}...`
2020
: plugin.description,
2121
)
2222
: theme.muted("(no description)");

src/hooks/fire-and-forget.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { logVerbose } from "../globals.js";
33
import { formatErrorMessage } from "../infra/errors.js";
44
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
55
import { resolveTimerTimeoutMs } from "../shared/number-coercion.js";
6+
import { truncateUtf16Safe } from "../utils.js";
67

78
const DEFAULT_MAX_CONCURRENT_FIRE_AND_FORGET_HOOKS = 16;
89
const DEFAULT_MAX_QUEUED_FIRE_AND_FORGET_HOOKS = 256;
@@ -72,7 +73,7 @@ export function formatHookErrorForLog(err: unknown): string {
7273
const formatted = replaceLogControlCharacters(formatErrorMessage(err))
7374
.replace(/\s+/g, " ")
7475
.trim();
75-
return (formatted || "unknown error").slice(0, MAX_HOOK_LOG_MESSAGE_LENGTH);
76+
return truncateUtf16Safe(formatted || "unknown error", MAX_HOOK_LOG_MESSAGE_LENGTH);
7677
}
7778

7879
/** Run a hook promise without awaiting it, logging rejection safely. */

0 commit comments

Comments
 (0)