Skip to content

Commit 89d8eb2

Browse files
committed
fix(discord): cover thread binding persona names
1 parent 0383908 commit 89d8eb2

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

extensions/discord/src/monitor/thread-bindings.persona.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ import {
66
} from "./thread-bindings.persona.js";
77
import type { ThreadBindingRecord } from "./thread-bindings.types.js";
88

9+
function hasLoneSurrogate(value: string): boolean {
10+
for (let index = 0; index < value.length; index += 1) {
11+
const code = value.charCodeAt(index);
12+
if (code >= 0xd800 && code <= 0xdbff) {
13+
const next = value.charCodeAt(index + 1);
14+
if (!(next >= 0xdc00 && next <= 0xdfff)) {
15+
return true;
16+
}
17+
index += 1;
18+
continue;
19+
}
20+
if (code >= 0xdc00 && code <= 0xdfff) {
21+
return true;
22+
}
23+
}
24+
return false;
25+
}
26+
927
describe("thread binding persona", () => {
1028
it("prefers explicit label and prefixes with gear", () => {
1129
expect(resolveThreadBindingPersona({ label: "codex thread", agentId: "codex" })).toBe(
@@ -32,4 +50,23 @@ describe("thread binding persona", () => {
3250
} satisfies ThreadBindingRecord;
3351
expect(resolveThreadBindingPersonaFromRecord(record)).toBe("⚙️ codex-thread");
3452
});
53+
it("keeps thread binding webhook personas on a UTF-16 boundary", () => {
54+
const record = {
55+
accountId: "default",
56+
channelId: "parent-1",
57+
threadId: "thread-1",
58+
targetKind: "acp",
59+
targetSessionKey: "agent:codex:acp:session-1",
60+
agentId: "codex",
61+
boundBy: "system",
62+
boundAt: Date.now(),
63+
lastActivityAt: Date.now(),
64+
label: `${"a".repeat(77)}\u{1f63e}tail`,
65+
} satisfies ThreadBindingRecord;
66+
67+
const persona = resolveThreadBindingPersonaFromRecord(record);
68+
69+
expect(persona.length).toBeLessThanOrEqual(80);
70+
expect(hasLoneSurrogate(persona)).toBe(false);
71+
});
3572
});

extensions/discord/src/monitor/thread-bindings.persona.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Discord plugin module implements thread bindings.persona behavior.
22
import { SYSTEM_MARK } from "openclaw/plugin-sdk/text-chunking";
3+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
34
import type { ThreadBindingRecord } from "./thread-bindings.types.js";
45

56
const THREAD_BINDING_PERSONA_MAX_CHARS = 80;
@@ -15,7 +16,7 @@ function normalizePersonaLabel(value: string | undefined): string | undefined {
1516
export function resolveThreadBindingPersona(params: { label?: string; agentId?: string }): string {
1617
const base =
1718
normalizePersonaLabel(params.label) || normalizePersonaLabel(params.agentId) || "agent";
18-
return `${SYSTEM_MARK} ${base}`.slice(0, THREAD_BINDING_PERSONA_MAX_CHARS);
19+
return truncateUtf16Safe(`${SYSTEM_MARK} ${base}`, THREAD_BINDING_PERSONA_MAX_CHARS);
1920
}
2021

2122
export function resolveThreadBindingPersonaFromRecord(record: ThreadBindingRecord): string {

0 commit comments

Comments
 (0)