fix(opencode): preserve Unicode in catalog tool input (#109591)

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Wynne668
2026-07-17 12:10:18 +08:00
committed by GitHub
parent 17c2ce05d8
commit 5f56a541d3
2 changed files with 23 additions and 2 deletions

View File

@@ -86,6 +86,7 @@ function captureOpenCodeSessionRegistrations(pluginConfig: unknown = {}) {
async function installFakeOpenCode(
assistantText = "hi",
sessionTitle = "Catalog session",
toolInput: unknown = { command: "pwd" },
): Promise<string> {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-opencode-catalog-"));
temporaryDirectories.push(directory);
@@ -125,7 +126,7 @@ async function installFakeOpenCode(
id: "prt_tool",
type: "tool",
tool: "bash",
state: { status: "completed", input: { command: "pwd" }, output: "/workspace" },
state: { status: "completed", input: toolInput, output: "/workspace" },
},
],
},
@@ -299,6 +300,25 @@ describe("OpenCode session catalog", () => {
},
);
it.runIf(process.platform !== "win32")(
"keeps truncated tool input on a valid UTF-16 boundary",
async () => {
await installFakeOpenCode("hi", "Catalog session", {
value: `${"x".repeat(19_989)}🎉`,
});
const transcript = await readLocalOpenCodeTranscriptPage({
threadId: "ses_test",
limit: 20,
});
const toolCall = transcript.items.find((item) => item.type === "toolCall");
expect(toolCall?.text).toMatch(/$/u);
expect(toolCall?.text).not.toMatch(
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u,
);
},
);
it.runIf(process.platform !== "win32")(
"auto-detects the CLI and honors the node-local Web UI switch",
async () => {

View File

@@ -6,6 +6,7 @@ import type {
SessionsCatalogReadResult,
} from "openclaw/plugin-sdk/session-catalog";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import {
materializeWindowsSpawnProgram,
resolveWindowsSpawnProgram,
@@ -352,7 +353,7 @@ export async function listLocalOpenCodeSessionPage(value?: unknown): Promise<Ope
function jsonText(value: unknown, maxLength = 20_000): string | undefined {
try {
const text = JSON.stringify(value);
return text.length > maxLength ? `${text.slice(0, maxLength)}` : text;
return text.length > maxLength ? `${truncateUtf16Safe(text, maxLength)}` : text;
} catch {
return undefined;
}