diff --git a/extensions/onepassword/src/cli.test.ts b/extensions/onepassword/src/cli.test.ts index d61dcf295aa4..4c61bfa02dc7 100644 --- a/extensions/onepassword/src/cli.test.ts +++ b/extensions/onepassword/src/cli.test.ts @@ -127,4 +127,37 @@ describe("1Password CLI output", () => { expect(rows[0]?.reason).toHaveLength(80); expect(JSON.stringify(rows)).not.toContain(["fixture", "value"].join("-")); }); + + it("preserves complete surrogate pairs at the audit reason boundary", async () => { + const store = new MemoryKeyedStore(); + await store.register("split", { + timestampMs: 1000, + agentId: "agent-a", + sessionKey: "session-a", + toolCallId: "call-a", + slug: "alpha", + reason: `${"x".repeat(76)}\u{1f600}tail`, + outcome: "auto", + }); + await store.register("fits", { + timestampMs: 2000, + agentId: "agent-a", + sessionKey: "session-a", + toolCallId: "call-b", + slug: "alpha", + reason: `${"x".repeat(75)}\u{1f600}tail`, + outcome: "auto", + }); + const { onepassword, write } = setupCommands(store); + + await onepassword.child("audit").run({ limit: "2" }); + const output = String(write.mock.calls[0]?.[0]); + expect(output).not.toContain("\\ud83d"); + const rows = JSON.parse(output) as Array>; + + expect(rows.map((row) => row.reason)).toEqual([ + `${"x".repeat(75)}\u{1f600}...`, + `${"x".repeat(76)}...`, + ]); + }); }); diff --git a/extensions/onepassword/src/cli.ts b/extensions/onepassword/src/cli.ts index b2ed0b71efb7..858a9d039b4f 100644 --- a/extensions/onepassword/src/cli.ts +++ b/extensions/onepassword/src/cli.ts @@ -1,4 +1,5 @@ import type { PluginStateKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime"; +import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; import type { AuditRow } from "./broker.js"; import type { OnePasswordConfig } from "./config.js"; import type { OpClient } from "./op-client.js"; @@ -30,7 +31,7 @@ function parseLimit(value: unknown): number { } function truncateReason(reason: string): string { - return reason.length <= 80 ? reason : `${reason.slice(0, 77)}...`; + return reason.length <= 80 ? reason : `${truncateUtf16Safe(reason, 77)}...`; } async function buildStatus(