fix(auto-reply): keep ACP steer output UTF-16 safe (#102583)

* fix(auto-reply): use truncateUtf16Safe in bash command and ACP steer output truncation

Replace naive .slice(0, N) with truncateUtf16Safe() in:
- bash-command.ts: shell command name preview
- lifecycle.ts: ACP steer output text

Co-Authored-By: Claude <noreply@anthropic.com>

* test(auto-reply): prove safe ACP steer output bounds

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
lsr911
2026-07-09 17:44:00 +08:00
committed by GitHub
parent 9bf212765f
commit 4792fa583f
2 changed files with 24 additions and 1 deletions

View File

@@ -1621,6 +1621,28 @@ describe("/acp command", () => {
expect(result?.reply?.text).toContain("Applied steering.");
});
it("keeps bounded ACP steer output UTF-16 safe", async () => {
const prefix = "a".repeat(799);
hoisted.callGatewayMock.mockImplementation(async (request: { method?: string }) => {
if (request.method === "sessions.resolve") {
return { key: defaultAcpSessionKey };
}
return { ok: true };
});
hoisted.readAcpSessionEntryMock.mockReturnValue(createAcpSessionEntry());
hoisted.runTurnMock.mockImplementation(async function* () {
yield { type: "text_delta", text: `${prefix}😀tail` };
yield { type: "done" };
});
const result = await runDiscordAcpCommand(
`/acp steer --session ${defaultAcpSessionKey} tighten logging`,
);
expect(result?.reply?.text).toContain(`\n${prefix}`);
expect(result?.reply?.text).not.toContain("😀");
});
it("resolves bound Telegram topic ACP sessions for /acp steer without explicit target", async () => {
hoisted.sessionBindingResolveByConversationMock.mockImplementation(
(ref: { channel?: string; accountId?: string; conversationId?: string }) =>

View File

@@ -5,6 +5,7 @@ import {
resolveAcpThreadSessionDetailLines,
} from "@openclaw/acp-core/runtime/session-identifiers";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { getAcpSessionManager } from "../../../acp/control-plane/manager.js";
import { resolveAcpSessionResolutionError } from "../../../acp/control-plane/manager.utils.js";
import {
@@ -787,7 +788,7 @@ async function runAcpSteer(params: {
if (event.text) {
output += event.text;
if (output.length > ACP_STEER_OUTPUT_LIMIT) {
output = `${output.slice(0, ACP_STEER_OUTPUT_LIMIT)}`;
output = `${truncateUtf16Safe(output, ACP_STEER_OUTPUT_LIMIT)}`;
}
}
},